溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

發(fā)布時(shí)間:2020-10-15 16:47:22 來(lái)源:億速云 閱讀:149 作者:小新 欄目:web開發(fā)

這篇文章主要介紹了svg path在網(wǎng)頁(yè)開發(fā)中如何使用,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。

SVG是矢量圖形表示,它的一個(gè)強(qiáng)大之處在于path標(biāo)簽可以表示任意的矢量形狀,利用好這個(gè)path可以做出很多傳統(tǒng)html/css做不出來(lái)的效果。下面舉幾個(gè)例子。

1. 做路徑動(dòng)畫

這個(gè)我在《SVG導(dǎo)航下劃線光標(biāo)跟隨效果》文后補(bǔ)充介紹了這個(gè)實(shí)現(xiàn),最后的效果是這樣的:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

實(shí)現(xiàn)代碼如下:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

利用animateMotion結(jié)合path做的動(dòng)畫,具體說(shuō)明可見上文。

2. 實(shí)現(xiàn)不規(guī)則形狀的點(diǎn)擊

如下圖所示,需要實(shí)現(xiàn)點(diǎn)到哪個(gè)洲就進(jìn)入哪個(gè)洲的效果,例如點(diǎn)到非洲就進(jìn)入非洲:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

我們可以用div定一個(gè)框蓋在非洲的上面,但是用div的話只能是規(guī)則的四方形,沒(méi)辦法實(shí)現(xiàn)點(diǎn)到非洲大陸的時(shí)候才進(jìn)入,但是大陸的輪廓又是不規(guī)則的,所以用傳統(tǒng)html是不能解決這個(gè)問(wèn)題的。但是用SVG的path可以解決這個(gè)問(wèn)題,方法1是監(jiān)聽path的點(diǎn)擊事件即可,如下圖所示:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

因?yàn)檫@個(gè)輪廓可以跟UI要到,他們一般都是用AI/PS等矢量軟件畫出來(lái)的,讓他們導(dǎo)一個(gè)SVG給你就好了。

方法2是可以調(diào)SVG的isPointInFill這個(gè)API判斷點(diǎn)擊的點(diǎn)是否在Path的fill區(qū)域里面,這個(gè)也可以實(shí)現(xiàn),但是相對(duì)于方法1來(lái)說(shuō)比較麻煩,因?yàn)檫€需要把鼠標(biāo)的位置轉(zhuǎn)換為svg視圖的位置。

3. 沿著路徑拖拽的交互

在第1點(diǎn)沿著路徑的動(dòng)畫是自動(dòng)的過(guò)程,有沒(méi)有辦法讓用戶自己拖拽過(guò)去呢,實(shí)現(xiàn)如下效果:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

這種的場(chǎng)景有音量控制等需要有百分比控制的。可以先用一個(gè)SVG的在線工具畫出一個(gè)這樣的圖形:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

就可以拿到SVG的代碼:

<svg class="volumn-controller" width="580" height="400" xmlns="http://www.w3.org/2000/svg">
    <path class="volumn-path" stroke="#000" d="m100,247c93,-128 284,-129 388,6" opacity="0.5" stroke-width="1" fill="#fff"/>
    <circle class="drag-button" r="12" cy="247" cx="100" stroke-width="1" stroke="#000" fill="#fff"/>
 </g>
</svg>

這里比較關(guān)鍵的是path標(biāo)簽里的d屬性,d是data的縮寫,定義這個(gè)路徑的形狀,它里面可以用很多屬性控制形狀的變化,如下圖所示:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

為了實(shí)現(xiàn)這個(gè)交互,需要?jiǎng)討B(tài)地改變circle的圓心位置(cx, cy)到路徑上相應(yīng)的地方。SVG沒(méi)有直接提供相關(guān)的API,但是它提供了一個(gè)可以間接利用的API叫g(shù)etPointAtLength,傳遞一個(gè)長(zhǎng)度參數(shù),如下代碼所示:

let volumnPath = document.querySelector('.volumn-path');
// 輸出path在長(zhǎng)度為100的位置的點(diǎn)坐標(biāo)
console.log(volumnPath.getPointAtLength(100));
// 輸出當(dāng)前path的總長(zhǎng)度
console.log(volumnPath.getTotalLength());

控制臺(tái)輸出:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

把circle的cx/cy改成上面的x/y坐標(biāo),圓圈就會(huì)跑到對(duì)應(yīng)的位置去了:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

這里的問(wèn)題在于這個(gè)API傳遞的length參數(shù)是相對(duì)于曲線長(zhǎng)度的,但是鼠標(biāo)移動(dòng)的位置是線性的,沒(méi)辦法直接知道當(dāng)前鼠標(biāo)在曲線上距離起始位置多少。

所以需要算一下,在這個(gè)場(chǎng)景里面我們可以取鼠標(biāo)的x坐標(biāo)在曲線上對(duì)應(yīng)的位置就可以了,如下圖示意:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

到這里就有思路了,可以把這條路徑上每隔一個(gè)像素長(zhǎng)度就算一下它的坐標(biāo)在哪里,然后存在一個(gè)數(shù)組里面。由于鼠標(biāo)移動(dòng)的時(shí)候x坐標(biāo)是知道的,就可以查一下在這個(gè)數(shù)組里面相應(yīng)x坐標(biāo)的y坐標(biāo)是多少,就能得到想要的圓心位置了。

所以先計(jì)算一下,保存到一個(gè)數(shù)組:

let $volumnController = document.querySelector('.volumn-controller'),
    $volumnPath = $volumnController.querySelector('.volumn-path');
// 得到當(dāng)前路徑的總長(zhǎng)度
let pathTotalLength = $volumnPath.getTotalLength() >> 0;
let points = [];
// 起始位置為長(zhǎng)度為0的位置
let startX = Math.round($volumnPath.getPointAtLength(0).x);
// 每隔一個(gè)像素距離就保存一下路徑上點(diǎn)的坐標(biāo)
for (let i = 0; i < pathTotalLength; i++) {
    let p = $volumnPath.getPointAtLength(i);
    // 保存的坐標(biāo)用四舍五入,可以平衡誤差
    points[Math.round(p.x) - startX] = Math.round(p.y);
}

這里用一個(gè)p0ints數(shù)組來(lái)保存,它的索引index就為x坐標(biāo),值為y坐標(biāo)。在這個(gè)例子里面,總長(zhǎng)度為451.5px,得到的points數(shù)組長(zhǎng)度為388. 你可以隔0.5px長(zhǎng)度就保存一個(gè)坐標(biāo),不過(guò)在這個(gè)例子里面1px就夠了。

然后監(jiān)聽鼠標(biāo)事件,得到x坐標(biāo),查詢y坐標(biāo),動(dòng)態(tài)地改變circle的圓心位置,如下代碼所示:

let $dragButton = $volumnController.querySelector('.drag-button'),
    // 得到起始位置相對(duì)當(dāng)前視窗的位置,相當(dāng)于jQuery.fn.offset
    dragButtonPos = $dragButton.getBoundingClientRect();
function movePoint (event) {
    // 當(dāng)前鼠標(biāo)的位置減去圓心起始位置就得到移位偏差,12是半徑值,這里先直接寫死
    let diffX = event.clientX - Math.round(dragButtonPos.left + 12);
    // 需要做個(gè)邊界判斷
    diffX < 0 && (diffX = 0);
    diffX >= points.length && (diffX = points.length - 1);
    // startX是在上面的代碼得到的長(zhǎng)度為0的位置
    $dragButton.setAttribute('cx', diffX + startX);
    // 使用points數(shù)組得到y(tǒng)坐標(biāo)
    $dragButton.setAttribute('cy', points[diffX]);
}
$dragButton.addEventListener('mousedown', function (event) {
    document.addEventListener('mousemove', movePoint);
});
document.addEventListener('mouseup', function () {
    document.removeEventListener('mousemove', movePoint);
});    					

這個(gè)實(shí)現(xiàn)的代碼也是比較簡(jiǎn)單,需要注意的地方是起始位置的選取,這里有兩個(gè)坐標(biāo)系,一個(gè)是相對(duì)頁(yè)面的視窗的,它的原點(diǎn)(0, 0)坐標(biāo)點(diǎn)是當(dāng)前頁(yè)面可視區(qū)域(client)的左上角,第二個(gè)坐標(biāo)系是SVG的坐標(biāo)系,它的原點(diǎn)(0, 0)位置是SVG畫布的左上角,如下圖所示:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

鼠標(biāo)的位置是相對(duì)于視圖client的,所以需要得到圓圈在client的位置,可以通過(guò)原生的getBoundingClient獲取,然后用鼠標(biāo)的clientX減掉圓圈的clientX就得到正確的位移偏差diff了,這個(gè)diff值加上圓圏的在svg坐標(biāo)的起始位置就能得到svg里的x坐標(biāo)了,然后去查一下points數(shù)組就能得到y(tǒng)坐標(biāo),然后去設(shè)置circle的cx/cy值。

這個(gè)的實(shí)現(xiàn)已經(jīng)算是十分簡(jiǎn)單的,大概30行代碼。需要注意的是如果svg縮放了,那么坐標(biāo)也要相應(yīng)比例地改一下。所以最好是不要縮放,1:1顯示就簡(jiǎn)單多了。

如果要顯示具體的音量值呢?這個(gè)也好辦,只需要在第一步保存點(diǎn)坐標(biāo)的時(shí)候把在路徑上的長(zhǎng)度也保存下來(lái)就好了,最后效果如下:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

如果路徑比較復(fù)雜怎么辦呢,一個(gè)x坐標(biāo)可能會(huì)對(duì)應(yīng)兩個(gè)點(diǎn),如下圖所示:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

這個(gè)也是有辦法的,計(jì)算的方法類似,也是需要把路徑上所有每隔1px的點(diǎn)坐標(biāo)都取出來(lái),然后計(jì)算一下鼠標(biāo)的位置距離哪個(gè)點(diǎn)的坐標(biāo)最接近,然后就取那個(gè)點(diǎn)就好了。當(dāng)然在判斷哪個(gè)點(diǎn)最優(yōu)時(shí),算法需要優(yōu)化,不能直接一個(gè)for循環(huán),具體可見這個(gè)codepen。

4. 路徑的變形動(dòng)畫

路徑結(jié)合關(guān)鍵幀可以做出一些有趣的效果,如這個(gè)codepen的示例:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

它的實(shí)現(xiàn)是hover的時(shí)候改變path的d值,然后做d的transition動(dòng)畫,如下代碼:

<svg viewBox="0 0 10 10" class="svg-1">
  <path d="M2,2 L8,8" />
</svg>
<style>
.svg-1:hover path {
  d: path("M8,2 L2,8");
}
path {
    transition: d 0.5s linear;
}
</style>

這種變形過(guò)渡動(dòng)畫是有條件的,就是它的路徑數(shù)據(jù)格式是要一致的,有多少個(gè)M/L/C屬性都要保持一致,否則無(wú)法做變形動(dòng)畫。

5. 結(jié)合clip-path做遮罩效果

使用CSS通常只能用border-radius做一些圓角的遮罩,即用border-radius結(jié)合overflow: hidden實(shí)現(xiàn),但是使用clip-path + svg的路徑能夠做出任意形狀遮罩,如下做一個(gè)心形的:

svg path在網(wǎng)頁(yè)開發(fā)中如何使用

如下代碼所示:

<div style="width:200px;height:200px">
    <img src="photo.png" alt style="width:100%">
</div>
<style>
img {
    clip-path: url("#heart");
}
</style>

style里面的id: #heart是指向了一個(gè)SVG的的clipPath,如下所示:

<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
    <clipPath id="heart" clipPathUnits="objectBoundingBox">
        <path transform="scale(0.0081967, 0.0101010)" d="m61.18795,24.08746c24.91828,-57.29309 122.5489,0 0,73.66254c-122.5489,-73.66254 -24.91828,-130.95562 0,-73.66254z"/>
    </clipPath>
</svg>   					

為了讓這個(gè)path剛好能撐起div容器寬度的100%,需要設(shè)置:

clipPathUnits="objectBoundingBox"

這樣會(huì)導(dǎo)致d屬性里面的單位變成比例的0到1,所以需要把它縮小一下,原本的width是122,height是99,需要需要scale的值為(1 / 122, 1 / 99)。這樣就達(dá)到100%占滿的目的,如果一開始d屬性坐標(biāo)比例就是0到1,就不用這么搞了。

另外clip-path使用svg的path不支持變形動(dòng)畫。

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享svg path在網(wǎng)頁(yè)開發(fā)中如何使用內(nèi)容對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,遇到問(wèn)題就找億速云,詳細(xì)的解決方法等著你來(lái)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI