开发者社区> 我是小助手> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

SVG Path路径在网页开发的作用

简介: SVG是矢量图形表示,它的一个强大之处在于path标签可以表示任意的矢量形状,利用好这个path可以做出很多传统html/css做不出来的效果。下面举几个例子。
+关注继续查看

SVG是矢量图形表示,它的一个强大之处在于path标签可以表示任意的矢量形状,利用好这个path可以做出很多传统html/css做不出来的效果。下面举几个例子。

1. 做路径动画

这个我在《SVG导航下划线光标跟随效果》文后补充介绍了这个实现,最后的效果是这样的:


476cf6ced7455bc0b6bfcd4d739ea1c3561f6bcb

利用animateMotion结合path做的动画,具体说明可见上文。

2. 实现不规则形状的点击

如下图所示,需要实现点到哪个洲就进入哪个洲的效果,例如点到非洲就进入非洲:

3aa0cb7797870d4831d25c83ca3907c6d86cc786

我们可以用div定一个框盖在非洲的上面,但是用div的话只能是规则的四方形,没办法实现点到非洲大陆的时候才进入,但是大陆的轮廓又是不规则的,所以用传统html是不能解决这个问题的。但是用SVG的path可以解决这个问题,方法1是监听path的点击事件即可,如下图所示:

f0c2fec1d83241b6dc8bc7f36dd47538c4630021

因为这个轮廓可以跟UI要到,他们一般都是用AI/PS等矢量软件画出来的,让他们导一个SVG给你就好了。

方法2是可以调SVG的isPointInFill这个API判断点击的点是否在Path的fill区域里面,这个也可以实现,但是相对于方法1来说比较麻烦,因为还需要把鼠标的位置转换为svg视图的位置。

3. 沿着路径拖拽的交互

在第1点沿着路径的动画是自动的过程,有没有办法让用户自己拖拽过去呢,实现如下效果:


a6f36b5a01ca8fd39a32100a579d8e3449ecf7a0
就可以拿到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>


这里比较关键的是path标签里的d属性,d是data的缩写,定义这个路径的形状,它里面可以用很多属性控制形状的变化,如下图所示:
9b8f4e393e7a5c8f5a1dacfe5ad6cc53004eb7fe

为了实现这个交互,需要动态地改变circle的圆心位置(cx, cy)到路径上相应的地方。SVG没有直接提供相关的API,但是它提供了一个可以间接利用的API叫getPointAtLength,传递一个长度参数,如下代码所示:

let volumnPath = document.querySelector('.volumn-path');
// 输出path在长度为100的位置的点坐标
console.log(volumnPath.getPointAtLength(100));
// 输出当前path的总长度
console.log(volumnPath.getTotalLength());

控制台输出:
32c36b1c4352ce6b632b178d5548e2dff3897f4b
把circle的cx/cy改成上面的x/y坐标,圆圈就会跑到对应的位置去了
070f1778a76ca15c75b3d42fc9da8141a099d087

到这里就有思路了,可以把这条路径上每隔一个像素长度就算一下它的坐标在哪里,然后存在一个数组里面。由于鼠标移动的时候x坐标是知道的,就可以查一下在这个数组里面相应x坐标的y坐标是多少,就能得到想要的圆心位置了。

所以先计算一下,保存到一个数组:


let $volumnController = document.querySelector('.volumn-controller'),
    $volumnPath = $volumnController.querySelector('.volumn-path');
// 得到当前路径的总长度
let pathTotalLength = $volumnPath.getTotalLength() >> 0;
let points = [];
// 起始位置为长度为0的位置
let startX = Math.round($volumnPath.getPointAtLength(0).x);
// 每隔一个像素距离就保存一下路径上点的坐标
for (let i = 0; i < pathTotalLength; i++) {
    let p = $volumnPath.getPointAtLength(i);
    // 保存的坐标用四舍五入,可以平衡误差
    points[Math.round(p.x) - startX] = Math.round(p.y);
}

这里用一个p0ints数组来保存,它的索引index就为x坐标,值为y坐标。在这个例子里面,总长度为451.5px,得到的points数组长度为388. 你可以隔0.5px长度就保存一个坐标,不过在这个例子里面1px就够了。

然后监听鼠标事件,得到x坐标,查询y坐标,动态地改变circle的圆心位置,如下代码所示:



let $dragButton = $volumnController.querySelector('.drag-button'),
    // 得到起始位置相对当前视窗的位置,相当于jQuery.fn.offset
    dragButtonPos = $dragButton.getBoundingClientRect();
function movePoint (event) {
    // 当前鼠标的位置减去圆心起始位置就得到移位偏差,12是半径值,这里先直接写死
    let diffX = event.clientX - Math.round(dragButtonPos.left + 12);
    // 需要做个边界判断
    diffX < 0 && (diffX = 0);
    diffX >= points.length && (diffX = points.length - 1);
    // startX是在上面的代码得到的长度为0的位置
    $dragButton.setAttribute('cx', diffX + startX);
    // 使用points数组得到y坐标
    $dragButton.setAttribute('cy', points[diffX]);
}
$dragButton.addEventListener('mousedown', function (event) {
    document.addEventListener('mousemove', movePoint);
});
document.addEventListener('mouseup', function () {
    document.removeEventListener('mousemove', movePoint);
});

这个实现的代码也是比较简单,需要注意的地方是起始位置的选取,这里有两个坐标系,一个是相对页面的视窗的,它的原点(0, 0)坐标点是当前页面可视区域(client)的左上角,第二个坐标系是SVG的坐标系,它的原点(0, 0)位置是SVG画布的左上角,如下图所示:

587241ace67ec8fcf0ae4986f8f8f377db695556

鼠标的位置是相对于视图client的,所以需要得到圆圈在client的位置,可以通过原生的getBoundingClient获取,然后用鼠标的clientX减掉圆圈的clientX就得到正确的位移偏差diff了,这个diff值加上圆圏的在svg坐标的起始位置就能得到svg里的x坐标了,然后去查一下points数组就能得到y坐标,然后去设置circle的cx/cy值。

这个的实现已经算是十分简单的,大概30行代码。需要注意的是如果svg缩放了,那么坐标也要相应比例地改一下。所以最好是不要缩放,1:1显示就简单多了。

如果要显示具体的音量值呢?这个也好办,只需要在第一步保存点坐标的时候把在路径上的长度也保存下来就好了,最后效果如下:


2deb01cab94d6d4e29d4301a7292bd6118d73802
50c655231e39ec9f6231411efb5b31693bec0ce1

这个也是有办法的,计算的方法类似,也是需要把路径上所有每隔1px的点坐标都取出来,然后计算一下鼠标的位置距离哪个点的坐标最接近,然后就取那个点就好了。当然在判断哪个点最优时,算法需要优化,不能直接一个for循环,具体可见这个codepen

4. 路径的变形动画

路径结合关键帧可以做出一些有趣的效果,如这个codepen的示例:


2ecadced2b5a1229fc02ccc833e88259d7406d8d
它的实现是hover的时候改变path的d值,然后做d的transition动画,如下代码:

<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>

这种变形过渡动画是有条件的,就是它的路径数据格式是要一致的,有多少个M/L/C属性都要保持一致,否则无法做变形动画。

5. 结合clip-path做遮罩效果

使用CSS通常只能用border-radius做一些圆角的遮罩,即用border-radius结合overflow: hidden实现,但是使用clip-path + svg的路径能够做出任意形状遮罩,如下做一个心形的:


4ef52c4af068417af287e793b02149df4688227f
如下代码所示:

<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是指向了一个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>

为了让这个path刚好能撑起div容器宽度的100%,需要设置:

clipPathUnits="objectBoundingBox"

这样会导致d属性里面的单位变成比例的0到1,所以需要把它缩小一下,原本的width是122,height是99,需要需要scale的值为(1 / 122, 1 / 99)。这样就达到100%占满的目的,如果一开始d属性坐标比例就是0到1,就不用这么搞了。

另外clip-path使用svg的path不支持变形动画。

本篇介绍了使用svg路径path做的几种效果:做一个路径动画、不规则形状的点击、沿着路径拖拽、路径的变形动画以及和clip-path做一些遮罩效果。可以说svg的path效果还是很强大的,当你有些效果用html/css无法实现的时候,不妨往svg的方向思考。



原文发布时间为:2018年06月17日
原文作者:人人网FED
本文来源:掘金 如需转载请联系原作者


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
QT5.14.2使用webkit引擎完成网页浏览
QT5.14.2使用webkit引擎完成网页浏览
210 0
标签之美八——网页框架
标签之美八——网页框架
62 0
网页的文档类型、标签及表格
什么是文档类型? 可以简单的理解为不同的html版本 从html4.0或4.01以后数字版本号基本固定,但是在此基础上又有分化: 1.严格型:可用的标签相对较少,但能兼容更多的浏览器 2.宽松型:可用的标签相对较多,但能兼容的浏览器较少 3.框架型:一种特殊需要的类型,只能在frameset标签的网页中使用 从html5 开始就删去了啰嗦的东西就剩 链接标签 _blank 新的窗口(空白窗口) _self: 当前网页本身这个窗口 _parent: 当前窗口的“上一级”窗口 ——学frameset的时候才能应用上。
871 0
日益增长的云市场面临的挑战
本文讲的是日益增长的云市场面临的挑战【IT168评论】每一个企业都意识到,云计算已经成为一个全球主导市场。一些人估计其价值在750亿美元,甚至更多,预计将继续增长。然而,尽管如此,越来越多的云市场面临着诸多挑战,保持竞争力。
1112 0
网页中常说的‘焦点’是什么意思
网页设计中常说的“焦点”可以理解为当前操作的对象。打个比方:某超级链接获得了焦点,则回车后,会打开这个超链。另外比如输入框获得焦点,即可在这输入框中录入文字,如果失去了焦点,就不是在这个输入框录入文字了。
840 0
scrapy 的三个入门应用场景
说明: 本文参照了官网的 dmoz 爬虫例子。 不过这个例子有些年头了,而 dmoz.org 的网页结构已经不同以前。所以我对xpath也相应地进行了修改。 概要: 本文提出了scrapy 的三个入门应用场景 爬取单页 根据目录页面,爬取所有指向的页面 爬取第一页,然后根据第一页的连接,再爬取下一页...。
941 0
C#调用默认浏览器打开网页的几种方法
CSharp调用默认浏览器打开网页的几种方法 示例界面: 方法一:从注册表中读取默认浏览器可执行文件路径 private void button1_Click(object sender, EventArgs e) { //从注册表中读...
748 0
+关注
我是小助手
云栖直播
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载