必威-必威-欢迎您

必威,必威官网企业自成立以来,以策略先行,经营致胜,管理为本的商,业推广理念,一步一个脚印发展成为同类企业中经营范围最广,在行业内颇具影响力的企业。

效果图如下必威,   译文出处

2019-09-30 05:59 来源:未知

Step7、将不同部位的动画组合到一起

  • 眼睛的动画是从嘴巴旋转完成开始,到嘴巴变形完成结束,因此和嘴巴的动画一样,我设置了四个对应的关键时间点。
  • 为了让衔接更顺畅,眼睛的动画开始比嘴巴变形开始稍微提前了一点点。

必威 1

参考:

  • MDN-SVG文档
  • 《SVG精髓》- 人民邮电出版社

    1 赞 2 收藏 评论

必威 2

将来的饼图


圆锥形渐变在这里也可以非常有帮助。它只需要一个圆形元素,以及带有两个色标的锥形渐变即可做出饼图。例如,图5中表示40%的饼图可以这样完成:

必威 3

CSS

.pie { width: 100px; height: 100px; border-radius: 50%; background: conic-gradient(#655 40%, yellowgreen 0); }

1
2
3
4
5
.pie {
  width: 100px; height: 100px;
  border-radius: 50%;
  background: conic-gradient(#655 40%, yellowgreen 0);
}

还有,一旦CSS Values Level 3中定义的attr()函数更新后被广泛应用,你就可以用简单的HTML属性来控制百分比了:

CSS

background: conic-gradient(#655 attr(data-value %), yellowgreen 0);

1
background: conic-gradient(#655 attr(data-value %), yellowgreen 0);

要添加第三种颜色也非常容易。例如,对于上面展示的饼图,我们只需要再增加两个色标:

CSS

background: conic-gradient(deeppink 20%, #fb3 0, #fb3 30%, yellowgreen 0);

1
background: conic-gradient(deeppink 20%, #fb3 0, #fb3 30%, yellowgreen 0);

:多亏了Lea的锥形渐变polyfill,我们现在才可以使用锥形渐变,在她的SmashingConf演讲结束不久之后发表的。这可能就是你将来用CSS来设计饼图的方式!这里的三种方法你会使用什么哪种,以及为什么这样做?或者你已经想到了一个完全不同的解决方案?请在评论中留言~

1 赞 2 收藏 评论

必威 4

2.14 <image>标签

<image>标签用于插入图片文件。

<svg viewBox="0 0 100 100" width="100" height="100"> <image xlink:href="path/to/image.jpg" width="50%" height="50%"/> </svg>

1
2
3
4
<svg viewBox="0 0 100 100" width="100" height="100">
  <image xlink:href="path/to/image.jpg"
    width="50%" height="50%"/>
</svg>

上面代码中,<image>xlink:href属性表示图像的来源。

填充——fill

fill 属性用于给形状填充颜色。

CSS

svg line { fill: #000; /* 填充黑色 */ }

1
2
3
svg line {
fill: #000; /* 填充黑色 */
}

填充色的透明度通过 fill-opacity 设置。

fill-rule 用于设置填充方式,算法较为抽象,除了 inherit 这个取值,还可取以下两种值:

nonzero:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的处的走向;计算结果从0开始,每有一个交点处的线段是从左到右的,就加1;每有一个交点处的线段是从右到左的,就减1;这样计算完所有交点后,如果这个计算的结果不等于0,则该点在图形内,需要填充;如果该值等于0,则在图形外,不需要填充。看下面的示例:

必威 5

evenodd:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的个数,个数为奇数则改点在图形内,需要填充;个数为偶数则点在图形外,不需要填充。看下图的示例:

必威 6

——引用来源《突袭 HTML5 之 SVG 2D 入门4 – 笔画与填充》

然而我们发现,我们的箭头即使填充了颜色,还是什么也看不见,问题就出在我们绘制的时候使用了没有面积的 line 标签。这个时候,就需要出动描边了。

本文主要想谈一谈前端关于渐变圆环的制作,效果图如下:

一个例子上手 SVG 动画

2017/05/05 · HTML5 · SVG

原文出处: 凹凸实验室   

CSS3动画已足够强大,不过还是有一些它做不到的地方。配合SVG,让Web动效有更多的可能性。这次要做的效果是一个loading动画(如图):其中旋转通过CSS来完成,但是旋转之后圆弧缩短变成笑脸的嘴巴需要借助SVG来实现。

必威 7

用CSS和SVG制作饼图

2015/08/10 · CSS · SVG

原文出处: Lea Verou   译文出处:lulux的博客   

在涉及到CSS技术时,没有人会比Lea Verou更执着、但是又足够聪明,努力去找寻问题的各种解决方案。最近,Lea自己撰写、设计和出版了一本书——CSS Secrets,这本书非常有趣,包括一些CSS小技巧以及解决常见问题的技术。如果你觉得自己的CSS技术还不错,看看这本书,你会吃惊的。在这篇文章中,我们发布了书里的一些片段,这也被发表在Lea最近在SmashingConf New York的演讲内容中——用CSS设计简单的饼图。注意,因为浏览器的支持有限,有些demo可能不能正常运行。——编辑

饼图,即使是最简单的只有两种颜色的形式,用Web技术创建也并不简单,尽管都是一些常见的信息内容,从简单的统计到进度条指标还有计时器。通常是使用外部图像编辑器来分别为多个值创建多个图像来实现,或是使用大型的JavaScript框架来设计更复杂的图表。

尽管这个东西并不像它曾经看起来那么难以实现,但是也没有什么直接并且简单的方法。但是,现在已经有很多更好、更易于维护的方式来实现它。

五、参考链接

  • Jon McPartland, An introduction to SVG animation
  • Alexander Goedde, SVG – Super Vector Graphics
  • Joseph Wegner, Learning SVG
  • biovisualize, Direct svg to canvas to png conversion
  • Tyler Sticka, Cropping Image Thumbnails with SVG
  • Adi Purdila, How to Create a Loader Icon With SVG Animations

(完)

1 赞 收藏 评论

必威 8

图形绘制

箭头的绘制只用到了路径中最简单的直线路径 line,SVG 中还有矩形 rect、圆形 circle、椭圆 ellipse、折线 polyline、多边形 polygon 以及万能的路径 path。之所以将一些规整的图形单独出标签,是为了代码的可读性更强些,毕竟 SVG 的可读性已经没那么强了……

规整图形的属性较好理解(具体可参考MDN-SVG Tutorial-Path),这里深入讲解一下如何阅读路径 path 的代码。

必威 9

Step5、给嘴巴部分添加动效

CSS

@keyframes mouth { 0% { transform: rotate(-80deg); stroke-dasharray: 60, 95; stroke-dashoffset: 0; } 40% { transform: rotate(280deg); stroke-dasharray: 60, 95; stroke-dashoffset: 0; } 70%, 100% { transform: rotate(280deg); stroke-dashoffset: -23; stroke-dasharray: 42, 95; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@keyframes mouth {
  0% {
    transform: rotate(-80deg);
    stroke-dasharray: 60, 95;
    stroke-dashoffset: 0;
  }
  40% {
    transform: rotate(280deg);
    stroke-dasharray: 60, 95;
    stroke-dashoffset: 0;
  }
  70%, 100% {
    transform: rotate(280deg);
    stroke-dashoffset: -23;
    stroke-dasharray: 42, 95;
  }
}

动画分为两个部分:

  1. 圆弧旋转
  2. 旋转之后缩短变形

在一个循环里,最后留有30%的时间保持一个停留。

必威 10

基于SVG的解决方案


SVG使得很多图形工作变得更加简单,饼图也不例外。但是,用path路径创建饼图,需要复杂的数学计算,我们可以使用一点小技巧来代替。

我们从一个圆开始:

<svg width="100" height="100"> <circle r="30" cx="50" cy="50" /> </svg>

1
2
3
<svg width="100" height="100">
<circle r="30" cx="50" cy="50" />
</svg>

现在,给它应用一些基础的样式:

CSS

circle { fill: yellowgreen; stroke: #655; stroke-width: 30; }

1
2
3
4
5
circle {
  fill: yellowgreen;
  stroke: #655;
  stroke-width: 30;
}

注意:你可能知道,这些CSS属性也可以作为SVG元素的属性使用,如果把可移植性考虑在内的话这可能挺方便的。

必威 11

图9:从一个绿色的SVG圆形,带一个胖胖的#655描边开始

你可以在图9中看到我们绘制的加了描边的圆。SVG描边不止有strokestroke-width属性。还有很多不是特别流行的描边相关的属性可以用于对描边进行微调。其中一个是stroke-dasharray,用于创建虚线描边。例如,我们可以使用如下:

CSS

stroke-dasharray: 20 10;

1
stroke-dasharray: 20 10;

必威 12

图10:一个简单的虚线描边,通过stroke-dasharray属性创建

这行代码的意思是我们的虚线是20的长度加上10的边距,如图10所示。在这里,你可能会好奇这个SVG描边属性和饼图究竟有什么关系呢。如果我们给描边应用一个值为0的虚线宽度,和一个大于或等于我们当前圆的周长的边距,它可能就清晰一些了(计算周长: C = 2πr , 所以在这里  C = 2π × 30 ≈ 189 ):

CSS

stroke-dasharray: 0 189;

1
stroke-dasharray: 0 189;

必威 13

图11:不同stroke-dasharray值对应的效果;从左到右: 0 189; 40 189; 95 189; 150 189 

如图11中的第一个圆所示,它的描边的都被移除了,只剩下一个绿色的圆。但是,当我们开始增大第一个值的时候,有趣的事情发生了(图11):因为边距太长,我们就没有虚线描边了,只有一个描边覆盖了我们指定的圆的周长的百分比。

你可能已经开始弄清楚了这是怎么回事:如果我们把圆的半径减小到一定程度,它可能就会完全被它的描边覆盖,最后得到的是一个非常类似于饼图的东西。例如,你可以在图12中看到:当给圆应用一个25的半径和一个50stroke-width,像下面的效果:

必威 14

图12:我们的SVG图像开始像一个饼图了O(∩_∩)O

记住:SVG描边总是相对于元素边缘一半在内一半在外的(居中的)。将来应该可以控制这一行为。

<svg width="100" height="100"> <circle r="25" cx="50" cy="50" /> </svg> circle { fill: yellowgreen; stroke: #655; stroke-width: 50; stroke-dasharray: 60 158; /* 2π × 25 ≈ 158 */ }

1
2
3
4
5
6
7
8
9
10
<svg width="100" height="100">
  <circle r="25" cx="50" cy="50" />
</svg>
 
circle {
  fill: yellowgreen;
  stroke: #655;
  stroke-width: 50;
  stroke-dasharray: 60 158; /* 2π × 25 ≈ 158 */
}

现在,把它变成我们在上一个解决方案中制作的饼图的样子是非常容易的:我们只需要在描边下面添加一个更大的绿色圆形,然后逆时针旋转90°,这样它的起点就在顶部中间。因为<svg>元素也是HTML元素,我们可以给它添加样式:

CSS

svg { transform: rotate(-90deg); background: yellowgreen; border-radius: 50%; }

1
2
3
4
5
svg {
  transform: rotate(-90deg);
  background: yellowgreen;
  border-radius: 50%;
}

必威 15

图13:最后的SVG饼图

你可以在图13中看到最终结果。这种技术可以让饼图更容易实现从0%100%变化的动画。我们只需要创建一个CSS动画,让stroke-dasharray从 0 158 变成 158 158 :

CSS

@keyframes fillup { to { stroke-dasharray: 158 158; } } circle { fill: yellowgreen; stroke: #655; stroke-width: 50; stroke-dasharray: 0 158; animation: fillup 5s linear infinite; }

1
2
3
4
5
6
7
8
9
10
11
@keyframes fillup {
  to { stroke-dasharray: 158 158; }
}
 
circle {
  fill: yellowgreen;
  stroke: #655;
  stroke-width: 50;
  stroke-dasharray: 0 158;
  animation: fillup 5s linear infinite;
}

作为一个额外的改进,我们可以在圆上指定一个特定半径,使其周长无限接近100,这样我们可以用百分比指定stroke-dasharray的长度,而不需要做计算。因为周长是2πr,我们的半径则是100 ÷ 2π ≈ 15.915494309,约等于16。我们还可以用viewBox特性指定SVG的尺寸,可以让它自动调整为容器的大小,不要使用widthheight属性。

经过以上调整,图13的饼图的HTML标签如下:

<svg viewBox="0 0 32 32"> <circle r="16" cx="16" cy="16" /> </svg>

1
2
3
<svg viewBox="0 0 32 32">
  <circle r="16" cx="16" cy="16" />
</svg>

CSS如下:

CSS

svg { width: 100px; height: 100px; transform: rotate(-90deg); background: yellowgreen; border-radius: 50%; } circle { fill: yellowgreen; stroke: #655; stroke-width: 32; stroke-dasharray: 38 100; /* for 38% */ }

1
2
3
4
5
6
7
8
9
10
11
12
13
svg {
  width: 100px; height: 100px;
  transform: rotate(-90deg);
  background: yellowgreen;
  border-radius: 50%;
}
 
circle {
  fill: yellowgreen;
  stroke: #655;
  stroke-width: 32;
  stroke-dasharray: 38 100; /* for 38% */
}

注意现在百分比已经可以很方便地改变了。当然,即使已经简化了标签,我们还是不想在绘制每个饼图的时候都重复一遍所有这些SVG标签。这是时候拿出JavaScript来帮我们一把了。我们写一个简单的脚本,让我们的HTML标签直接简单地这样写:

<div class="pie">20%</div> <div class="pie">60%</div>

1
2
<div class="pie">20%</div>
<div class="pie">60%</div>

然后在每个.pie元素里边添加一个内联SVG,包括所有需要的元素和属性。它还会添加一个<title>元素,为了增加可访问性,这样屏幕阅读器用户还可以知道当前的饼图表示的百分比。最后的脚本如下:

JavaScript

$$('.pie').forEach(function(pie) { var p = parseFloat(pie.textContent); var NS = ""; var svg = document.createElementNS(NS, "svg"); var circle = document.createElementNS(NS, "circle"); var title = document.createElementNS(NS, "title"); circle.setAttribute("r", 16); circle.setAttribute("cx", 16); circle.setAttribute("cy", 16); circle.setAttribute("stroke-dasharray", p + " 100"); svg.setAttribute("viewBox", "0 0 32 32"); title.textContent = pie.textContent; pie.textContent = ''; svg.appendChild(title); svg.appendChild(circle); pie.appendChild(svg); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$$('.pie').forEach(function(pie) {
  var p = parseFloat(pie.textContent);
  var NS = "http://www.w3.org/2000/svg";
  var svg = document.createElementNS(NS, "svg");
  var circle = document.createElementNS(NS, "circle");
  var title = document.createElementNS(NS, "title");
  circle.setAttribute("r", 16);
  circle.setAttribute("cx", 16);
  circle.setAttribute("cy", 16);
  circle.setAttribute("stroke-dasharray", p + " 100");
  svg.setAttribute("viewBox", "0 0 32 32");
  title.textContent = pie.textContent;
  pie.textContent = '';
  svg.appendChild(title);
  svg.appendChild(circle);
  pie.appendChild(svg);
});

就是它了!你可能会觉得CSS方法比较好,因为它的代码比较简单而且更靠谱。但是,SVG方法相比纯CSS方案还是有一定的优势的:

  • 可以非常容易地添加第三种颜色:只需要添加另一个描边圆,然后使用stroke-dashoffset设置它的描边属性。然后,将它的描边长度添加到下方的圆的描边长度上。如果是前面那个CSS的方案,你要如何给饼图添加第三种颜色呢?
  • 我们不需要考虑打印的问题,因为SVG元素就像<img>元素一样,被默认为是内容的一部分,打印完全没有问题。第一种方案取决于背景,所以不会被打印。
  • 我们可以使用内联样式改变颜色,也就是说我们可以通过脚本就直接改变颜色(如,根据用户输入改变颜色)。第一种方案依赖于伪元素,除了通过继承,它没有其它办法可以添加内联样式,这很不方便。

See the Pen oXVBar by Airen (@airen) on CodePen.

2.11 <g>标签

<g>标签用于将多个形状组成一个组(group),方便复用。

<svg width="300" height="100"> <g id="myCircle"> <text x="25" y="20">圆形</text> <circle cx="50" cy="50" r="20"/> </g> <use href="#myCircle" x="100" y="0" fill="blue" /> <use href="#myCircle" x="200" y="0" fill="white" stroke="blue" /> </svg>

1
2
3
4
5
6
7
8
9
<svg width="300" height="100">
  <g id="myCircle">
    <text x="25" y="20">圆形</text>
    <circle cx="50" cy="50" r="20"/>
  </g>
 
  <use href="#myCircle" x="100" y="0" fill="blue" />
  <use href="#myCircle" x="200" y="0" fill="white" stroke="blue" />
</svg>

stroke-dashoffset

(敲黑板)这个也是重点属性!
当我们将描边虚实设置成实线部分与图形描边长度相同时,我们是看不到空白段的部分的。这时形状的描边就像完全描绘出来了一样。这时我们使用这个属性,将虚线开始的位置稍微做一下移动,无论是往前移还是往后移,我们都能看到图形描边出现了一段空白,当这个移动形成一个连续的动作时,描线动效就这么不经意的出现了(蓦然回首)。

CSS

svg line { stroke-dasharray: 60; stroke-dashoffset: 60; transition: stroke-dashoffset ease-in .5s; }   svg:hover line { stroke-dashoffset: 0; }

1
2
3
4
5
6
7
8
9
svg line {
stroke-dasharray: 60;
stroke-dashoffset: 60;
transition: stroke-dashoffset ease-in .5s;
}
&nbsp;
svg:hover line {
stroke-dashoffset: 0;
}

必威 16

再对头部做个延时处理,修改一下虚线移动的方向,动效看起来会更顺眼一些。这个时候,SVG 可以分路径编辑的优势就体现出来了。对每个 line 添加一个类,我们就能对每条路径进行差异化处理(Codepen)。

XHTML

<svg width="360" height="52" viewBox="0 0 89.4 12.4"> <line class="arrow-line" x1="0" y1="6.2" x2="59.6" y2="6.2"></line> <line class="arrow-head" x1="54.7" y1="0.7" x2="60.5" y2="6.5"></line> <line class="arrow-head" x1="54.7" y1="11.7" x2="60.5" y2="5.8"></line> </svg>

1
2
3
4
5
<svg width="360" height="52" viewBox="0 0 89.4 12.4">
<line class="arrow-line" x1="0" y1="6.2" x2="59.6" y2="6.2"></line>
<line class="arrow-head" x1="54.7" y1="0.7" x2="60.5" y2="6.5"></line>
<line class="arrow-head" x1="54.7" y1="11.7" x2="60.5" y2="5.8"></line>
</svg>

CSS

svg line { fill: #000; stroke: #000; stroke-width: 1px; } .arrow-line { stroke-dasharray: 60; stroke-dashoffset: 60; transition: stroke-dashoffset ease-in .5s .2s; } .arrow-head { stroke-dasharray: 9; stroke-dashoffset: -9; transition: stroke-dashoffset ease-in .2s; } svg:hover line { stroke-dashoffset: 0; } svg:hover .arrow-line { transition: stroke-dashoffset ease-in .5s; } svg:hover .arrow-head { transition: stroke-dashoffset ease-in .2s .5s; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
svg line {
fill: #000;
stroke: #000;
stroke-width: 1px;
}
.arrow-line {
stroke-dasharray: 60;
stroke-dashoffset: 60;
transition: stroke-dashoffset ease-in .5s .2s;
}
.arrow-head {
stroke-dasharray: 9;
stroke-dashoffset: -9;
transition: stroke-dashoffset ease-in .2s;
}
svg:hover line {
stroke-dashoffset: 0;
}
svg:hover .arrow-line {
transition: stroke-dashoffset ease-in .5s;
}
svg:hover .arrow-head {
transition: stroke-dashoffset ease-in .2s .5s;
}

必威 17

了解了这两个重点属性,动效剩下的重担,就落在了 dasharray 与 dashoffset 值的计算上了。这个步骤或许没有什么捷径,简单的直线、弧线之类的或许还能口算口算,其余的不规则图形也就只有多试这条傻路可走,如果你是图形高手就当我没说。

另外三个描边属性:stroke-linecapstroke-linejoinstroke-miterlimit 由于暂时用不上惨遭抛弃,具体可参考MDN-SVG Tutorial-Fills and Strokes,stroke-miterlimit 详解SVG 研究之路(16)- Stroke-miterlimit。

注意:部分iphone 不支持css3旋转大于等于90度,故页面中所需旋转的均改为了89deg;

SVG有两种方式写渐变的:线性渐变linearGradient或放射性渐变radialGradient,但是却不能通过这两种方式实现 角度渐变 (当然,如果有人能实现角度渐变,非常欢迎推荐)

我是通过借助SVG能填充图片的功能实现,为实现上述渐变圆环,我的填充图如下:

必威 18

填充图片

关于SVG如何给路径填充背景图在此就不再细说,可以查看教程,相关教程有很多

直接上代码:

<svg width="100%" height="100%" viewbox="0 0 160 160"></svg>

如果是适配手机,需要等比例缩放,这就需要设置viewbox,表示svg的左上角坐标和宽高

<defs>

<pattern id="fill-img" patternUnits="userSpaceOnUse" width="160" height="160">

<image class="img-transform" xlink:href=""

             x="0"y="0"width="160"height="160">

</image>

</pattern>

</defs>

其中的样式 定义如下:

.img-transform{

        transform:rotate(89deg);

        transform-origin:50%50%;

}

背景图已经定义完成,接下来就是如果绘制圆环并使用背景图了,代码如下:

<circle class="circle-transform mapSvgDraw delay2-0" fill="none" stroke="url(#fill-imgs4)" stroke-width="15" stroke-miterlimit="1" cx="80" cy="80" r="72" stroke-dasharray="380 1000" stroke-dashoffset="380" stroke-linecap="round"/>

stroke-dasharray指定短划线和缺口的长度,如果提供了奇数个值,则这个值的数列重复一次,从而变成偶数个值

stroke-dashoffset:指定了dash模式到路径开始的距离,一般通过此值形成动画

其中的circle-transform 定义的是当前圆环所要旋转或位移的量,样式如下:

.circle-transform{

        transform:translate3d(-1px,-1px,0)rotate(-89deg);

        transform-origin:50%50%;

}

不管是图片或圆环的样式定义,都可以根据自己的需要去定义

到目前为止,圆环已成,如果需要添加动画效果,可参考以下代码

.mapSvgDraw{

// stroke-dasharray: 1000;

// stroke-dashoffset: 1000;

         -webkit-animation: mapSvgDraw1slinearboth;

          animation: mapSvgDraw1slinearboth;

}

@-webkit-keyframes mapSvgDraw {

        0%{}

        100%{

                 stroke-dashoffset:0;

         }

}

@keyframes mapSvgDraw{

          0%{}

          100%{

                   stroke-dashoffset:0;

           }

}

以上,即为所有代码,如有不懂,可查阅相关资料

参考文献:

文档信息

版权声明:自由转载-非商用-非衍生-保持署名

发表日期:2017年3月13日

Step2、绘制购物袋

购物袋由两个部分组成,先画下面的主体:

XHTML

<path d="M 20 40 L 80 40 L 80 90 A 10 10 90 0 1 70 100 L 30 100 A 10 10 90 0 1 20 90" style="fill: #e9e8ee;" />

1
<path d="M 20 40 L 80 40 L 80 90 A 10 10 90 0 1 70 100 L 30 100 A 10 10 90 0 1 20 90" style="fill: #e9e8ee;" />

任何形状都可以使用路径元素画出,描述轮廓的数据放在它的d属性中。
a.样式中的fill用来设置填充色。
b.路径数据由命令和坐标构成:

指令 说明
M 20 40 表示移动画笔到(20,40)
L 80 40 表示绘制一条线到(80, 40)
A 10 10 90 0 1 70 100 绘制一个椭圆弧

圆弧命令以字母A开始,后面紧跟着7个参数,这7个参数分别用来表示:

  • 椭圆的x半径和y半径
  • 椭圆的x轴旋转角度
  • 圆弧的角度小于180度,为0;大于或等于180度,则为1
  • 以负角度绘制为0,否则为1
  • 终点的x、y坐标

必威 19

接下来绘制购物袋上面的部分

XHTML

<path d="M 35 40 A 15 15 180 1 1 65 40" style="fill: none; stroke: #e9e8ee; stroke-width: 5;” />

1
<path d="M 35 40 A 15 15 180 1 1 65 40" style="fill: none; stroke: #e9e8ee; stroke-width: 5;” />

上面的部分是一个半圆弧,我同样用路径来画出,也可以使用基础形状来完成。

样式中的stokestroke-width分别用来设置描边色和描边的宽度。

必威 20

相关资源


  • CSS Transforms
  • CSS Image Values
  • CSS Backgrounds & Borders
  • Scalable Vector Graphics
  • CSS Image Values Level 4

二、语法

从一个简单的例子说起

必威 21

要做出这样的效果,第一步是将图形画出来。徒手敲代码这种事还是留给图形工具来做,但是,为了更好地控制与制作动效,咱至少要做到读懂 SVG 代码。

SVG 的基本格式是使用 <svg> 标签对代码进行包裹,可直接将代码段插入 html 中,也可以保存成 svg 文件之后使用 imgobject 进行引用。

XHTML

<svg width="100%" height="100%"> <!-- SVG markup here. --> </svg>

1
2
3
<svg width="100%" height="100%">
<!-- SVG markup here. -->
</svg>

由于交互动效所需,这里仅介绍直接使用 svg 标签的情况。

XHTML

<svg width="90" height="13" viewBox="0 0 89.4 12.4"> <line x1="0" y1="6.2" x2="59.6" y2="6.2"></line> <line x1="54.7" y1="0.7" x2="60.5" y2="6.5"></line> <line x1="54.7" y1="11.7" x2="60.5" y2="5.8"></line> </svg>

1
2
3
4
5
<svg width="90" height="13" viewBox="0 0 89.4 12.4">
<line x1="0" y1="6.2" x2="59.6" y2="6.2"></line>
<line x1="54.7" y1="0.7" x2="60.5" y2="6.5"></line>
<line x1="54.7" y1="11.7" x2="60.5" y2="5.8"></line>
</svg>

这是箭头的代码段,使用了最简单的线条进行绘制。可以看到其中包裹了许多坐标样的属性值。有坐标就意味着有坐标系。

SVG 的坐标系存在三个概念:视窗、视窗坐标系、用户坐标系。视窗坐标系与用户坐标系属于 SVG 的两种坐标系统,默认情况下这两个坐标系的点是一一对应的。与 web 其他坐标系相同,原点位于视窗的左上角,x 轴水平向右,y 轴垂直向下。

必威 22

(图片来源:MDN-SVG Tutorial-Positions)

SVG 的位置、大小与文档流中的块级元素相同,都可由 CSS 进行控制。

视窗即为在页面中 SVG 设定的尺寸可见部分,默认情况下 SVG 超出隐藏。

SVG 能通过 viewBox 属性就完成图形的位移与缩放。

viewBox属性值的格式为(x0,y0,u_width,u_height),每个值之间用逗号或者空格隔开,它们共同确定了视窗显示的区域:视窗左上角坐标设为(x0,y0)、视窗的宽设为 u_width,高为 u_height;这个变换对整个视窗都起作用。

下图展示了当 viewBox 尺寸与 SVG 尺寸相同、放大一倍、缩小一倍时的表现:

必威 23

必威 24

必威 25

一句话总结,就是用户坐标系需要以某种方式铺满整个视窗。默认的方式是以最短边为准铺满,也就是类似 background-size 中的 cover 值。通过 preserveAspectRatio 属性你可以控制用户坐标系的展开方式与位置,完美满足你的各种需求。

preserveAspectRatio 是一個以對齊為主,然後再選擇要自動填滿還是咖掉的屬性。——引用来源《SVG 研究之路 (23) – 理解 viewport 與 viewbox》

属性的语法如下:preserveAspectRatio="[defer] <align> [<meetOrSlice>]"

注意3个参数之间需要使用空格隔开。

defer:可选参数,只对 image 元素有效,如果 image 元素中 preserveAspectRatio 属性的值以 defer 开头,则意味着 image 元素使用引用图片的缩放比例,如果被引用的图片没有缩放比例,则忽略 defer。所有其他的元素都忽略这个字符串。

meetOrSlice:可选参数,可以去下列值:

  • meet – 默认值,统一缩放图形,让图形全部显示在 viewport 中。
  • slice – 统一缩放图形,让图形充满 viewport,超出的部分被剪裁掉。

——引用来源《突袭 HTML5 之 SVG 2D 入门6 – 坐标与变换》

align:必选参数。由两个名词组成。

這兩個名詞分別代表 viewbox 與 viewport 的 x 方向對齊模式,以及 y 方向的對齊模式,換句話說,可以想成:「水平置中 + 垂直靠上對齊」的這種感覺,不過在這個 align 的表現手法倒是很抽象,可以用下方的表格看出端倪:

必威 26

也因此我們要做一個「水平置中 + 垂直靠上對齊」的 viewbox 設定,就必須寫成:xMidYMin,做一個「水平靠右對齊 + 垂直靠下對齊」的 viewbox 設定,就必須寫成:xMaxYMax,不過這裡有個細節請特別注意,「Y」是大寫呀!真是不知道為什麼會這樣設計,我想或許跟命名規則有關吧!

——引用来源《SVG 研究之路 (23) – 理解 viewport 與 viewbox》

下图诠释了各种填充的效果:

必威 27

(图片来源:7 Coordinate Systems, Transformations and Units)

在这一层面处理好图形的展示之后,剩下的所有变换,无论是 translate、rotate 还是 opacity,我们都可以全权交给 CSS 来处理,并且可以将图形细化到形状或者路径的层面进行变换。

然而实际情况是,刚才的那段代码,放进codepen之后是什么也看不见的,原因就在于这个路径的绘制既没有填充颜色也没有描边。

此为圆环的静态图

Step1、声明SVG视口

XHTML

<svg width="100" height=“100”></svg>

1
<svg width="100" height=“100”></svg>

指定一个宽高都为100像素的区域,width=”100”和width=”100px”是等价的,当然也可以使用其他的合法单位,例如cm、mm、em等

阅读器会设置一个默认的坐标系统,见图:左上角为原点,其中水平(x)坐标向右递增,垂直(y)坐标向下递增。

必威 28

在没有指定的情况下,所有的的数值默认单位都是像素。

基于变换的解决方案


这个方案从HTML的角度来说是最好的:它只需要一个元素,其它的都可以用伪元素、变换和CSS渐变完成。我们从下面这个简单的元素开始:

<div class="pie"></div>

1
<div class="pie"></div>

现在,假设我们希望显示一个 20% 比例的饼图。灵活性的问题我们后面再解决。我们先给元素添加样式,让它变成一个圆,也就是我们的背景:

必威 29

图1:第一步是先画一个圆(或者可以说是显示0%比例的饼图)

CSS

.pie { width: 100px; height: 100px; border-radius: 50%; background: yellowgreen; }

1
2
3
4
5
.pie {
  width: 100px; height: 100px;
  border-radius: 50%;
  background: yellowgreen;
}

 

我们的饼图是绿色(特指 yellowgreen )和棕色( #655 )显示的百分比。可能会在比例部分尝试使用 transform 中的 skew ,但是经过几次试验之后表明,这是一个非常混乱的方案。因此,我们用这两种颜色为这个饼图的左右部分分别着色,然后对于我们想要的百分比,使用旋转的伪元素来实现。

我们使用一个简单的线性渐变,给右半部分着棕色:

CSS

background-image: linear-gradient(to right, transparent 50%, #655 0);

1
background-image: linear-gradient(to right, transparent 50%, #655 0);

必威 30

图2:用一个简单的线性渐变给右半圆着棕色

如图2所示,这样就完成了。现在,我们可以继续为伪元素添加样式,让它成为一个蒙版:

CSS

.pie::before { content: ''; display: block; margin-left: 50%; height: 100%; }

1
2
3
4
5
6
.pie::before {
  content: '';
  display: block;
  margin-left: 50%;
  height: 100%;
}

必威 31

图3:虚线内的内容表示伪元素将作为蒙版的区域

你可以在图3中看到我们的伪元素当前定位相对于我们的pie元素。目前,它还没有添加样式,也没有覆盖任何东西,只是一个透明的矩形。在开始添加样式之前,我们先来分析一下:

  • 因为我们希望它覆盖圆的棕色部分,我们需要给它应用一个绿色的背景,使用 background-color: inherit 来避免重复定义,因为我们本来就希望它和父元素的背景颜色保持一致。
  • 我们希望它绕着圆的中心点旋转,中心点在伪元素的左边,所以我们需要给它的 transform-origin ,应用一个 0 50% ,或者是直接一个 left 。
  • 我们不想要它是一个矩形,因为它会超过饼图的边缘,所以我们需要给 .pie 应用 overflow: hidden ,或者是一个恰当的 border-radius 让它成为一个半圆。

综上所述,伪元素的CSS样式如下:

CSS

.pie::before { content: ''; display: block; margin-left: 50%; height: 100%; border-radius: 0 100% 100% 0 / 50%; background-color: inherit; transform-origin: left; }

1
2
3
4
5
6
7
8
9
.pie::before {
  content: '';
  display: block;
  margin-left: 50%;
  height: 100%;
  border-radius: 0 100% 100% 0 / 50%;
  background-color: inherit;
  transform-origin: left;
}

必威 32

图4:添加样式之后的伪元素(这里用虚线表示)

注意:不要使用 class="crayon-syntax crayon-syntax-inline crayon-theme-github crayon-theme-github-inline crayon-font-monaco" style="font-size: 13px !important; line-height: 15px !important;font-size: 13px !important;"> class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 15px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"> class="crayon-e">background class="crayon-sy">: class="crayon-i">inherit class="crayon-sy">; ,要用 id="crayon-5b8f6c8720464547585400" class="crayon-syntax crayon-syntax-inline crayon-theme-github crayon-theme-github-inline crayon-font-monaco" style="font-size: 13px !important; line-height: 15px !important;font-size: 13px !important;"> class="crayon-pre crayon-code" style="font-size: 13px !important; line-height: 15px !important;font-size: 13px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;"> class="crayon-e">background-color class="crayon-sy">: class="crayon-i">inherit ;,否则父元素背景图像上的渐变也会被继承

我们的饼图目前如图4所示。现在开始有趣起来了!我们可以开始旋转伪元素,给它应用一个 rotate() 变换。要显示 20% 的比例,我们可以给它一个 72deg ( 0.2 x 360 = 72 ),或 .2turn ,这个可读性更好。你可以在图5中看到不同旋转角度值的结果。

必威 33

图5:分别展示不同百分比的饼图,从左到右: 10%  ( 36deg 或 .1turn ), 20%  ( 72deg 或  .2turn ), 40%  ( 144deg  或 .4turn )

你可能会想我们已经完成了,但是它可没这么简单。我们的饼图在展示050%的大小的内容时是没有任何问题的,但是如果我们要描绘一个 60% 的旋转(通过应用 .6turn ),就会发生如图6的情况。但是,别担心,我们可以解决这个事情!

必威 34

图6:对于超过50%的比例,我们的饼图就跪了orz(这里的是60%)

如果我们把 50%-100% 比例的情况作为单独的一个问题,可能会注意到可以使用之前的解决方案的反相版本:从0.5turn旋转的棕色伪元素。所以,对于一个60%的饼图,伪元素的CSS代码如下:

CSS

.pie::before { content: ''; display: block; margin-left: 50%; height: 100%; border-radius: 0 100% 100% 0 / 50%; background: #655; transform-origin: left; transform: rotate(.1turn); }

1
2
3
4
5
6
7
8
9
10
.pie::before {
  content: '';
  display: block;
  margin-left: 50%;
  height: 100%;
  border-radius: 0 100% 100% 0 / 50%;
  background: #655;
  transform-origin: left;
  transform: rotate(.1turn);
}

必威 35

图7: 60% 饼图的正确打开方式~

你可以在图7中看到结果。因为我们已经制定了一个可以描绘出任何百分比的方法,我们甚至可以为饼图从0%100%添加动画效果,创建出一个有趣的进度条:

CSS

@keyframes spin { to { transform: rotate(.5turn); } } @keyframes bg { 50% { background: #655; } } .pie::before { content: ''; display: block; margin-left: 50%; height: 100%; border-radius: 0 100% 100% 0 / 50%; background-color: inherit; transform-origin: left; animation: spin 3s linear infinite, bg 6s step-end infinite; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@keyframes spin {
  to { transform: rotate(.5turn); }
}
 
@keyframes bg {
  50% { background: #655; }
}
 
.pie::before {
  content: '';
  display: block;
  margin-left: 50%;
  height: 100%;
  border-radius: 0 100% 100% 0 / 50%;
  background-color: inherit;
  transform-origin: left;
  animation: spin 3s linear infinite,
             bg 6s step-end infinite;
}

 

See the Pen zGbNLJ by Airen (@airen) on CodePen.

显示没有问题,但是我们如果给多个不同百分比的静态饼图添加样式呢,最常见的用例是?在理想情况下,我们希望可以简单地输入如下的内容:

<div class="pie">20%</div> <div class="pie">60%</div>

1
2
<div class="pie">20%</div>
<div class="pie">60%</div>

然后就可以得到两个饼图,一个表示20%,一个表示60%。首先,我们先研究一下如何使用内联样式来完成,然后我们可以写一个简短的脚本来解析文本内容,对应地添加内联样式,而且要代码优雅、封装、可维护性,还有最重要的一点,可访问性。

使用内联样式控制饼图百分比的一个困难是:用于设置百分比CSS代码是用伪元素完成的。而且你也知道,我们不能给伪元素设置内联样式,所以我们需要创新。

注意:如果你想要使用的值是在某个不需要经过重复的复杂的计算的范围内的情况,你可以使用相同的技术,包括通过它们一步一步调试动画的情况。看该技术的一个简单的示例。

 

See the Pen YXgNOK by Airen (@airen) on CodePen.

解决方案来自最不可能的地方之一。我们将要使用我们已经介绍过的动画,但是它是暂停状态的。我们不会让它像一个正常的动画那样运行,我们将使用负延迟来让它可以静态地暂停在某个点。很奇怪?一个负的animation-delay的值不仅在规范中是允许的,在类似这样的案例中也是非常好用:

负延迟是有效的。和0s的延迟类似,它表示动画将立即执行,但是是根据延迟的绝对值来自动运行的,所以如果动画已经在指定的时间之前就开始运行了,那它就会直接从active的时间中途运行。 —CSS Animations Level 1

因为我们的动画是暂停的,它的第一帧就是我们唯一展示的那一帧(通过我们的animation-delay定义)。饼图上显示的百分比将会是我们的animation-delay的总时间。例如,当前的持续时间是6s,我们的 animation-delay 值为-1.2s则显示20%的百分比。为了简化计算,我们设置一个100s的持续时间。记住因为我们的动画是永远暂停的,我们给它指定的延迟大小并不会有什么影响。

还有最后一个问题:动画是赋给伪元素的,但是我们想要给.pie元素设置内联样式。因为<div>上没有动画,我们可以给它设置animation-delay作为内联样式,然后给伪元素应用 animation-delay: inherit; 。综上所述,20%60%的饼图的HTML代码如下:

<div class="pie" style="animation-delay: -20s"></div> <div class="pie" style="animation-delay: -60s"></div>

1
2
<div class="pie" style="animation-delay: -20s"></div>
<div class="pie" style="animation-delay: -60s"></div>

刚刚提出的这个动画的CSS代码如下(省略 .pie 规则,因为没有改变):

CSS

@keyframes spin { to { transform: rotate(.5turn); } } @keyframes bg { 50% { background: #655; } } .pie::before { /* [Rest of styling stays the same] */ animation: spin 50s linear infinite, bg 100s step-end infinite; animation-play-state: paused; animation-delay: inherit; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@keyframes spin {
  to { transform: rotate(.5turn); }
}
 
@keyframes bg {
  50% { background: #655; }
}
 
.pie::before {
  /* [Rest of styling stays the same] */
  animation: spin 50s linear infinite, bg 100s step-end infinite;
  animation-play-state: paused;
  animation-delay: inherit;
}

这时候,可以把HTML标签改成使用百分比作为内容,和一开始希望的一样,然后通过一个简单的脚本为其添加 animation-delay 内联样式。

JavaScript

$$('.pie').forEach(function(pie) { var p = parseFloat(pie.textContent); pie.style.animationDelay = '-' + p + 's'; });

1
2
3
4
$$('.pie').forEach(function(pie) {
  var p = parseFloat(pie.textContent);
  pie.style.animationDelay = '-' + p + 's';
});

必威 36

图8:没有隐藏文本前的图

  • 把饼图的height转换成 line-height (或添加一个和height值相等的line-height,但是这值是毫无意义的重复代码,因为line-height会自动计算height的值)。
  • 通过绝对定位给伪元素设置大小和位置,这样它不会把文本挤下去。
  • 添加 text-align: center; 让文本水平居中。

最后的代码如下:

CSS

.pie { position: relative; width: 100px; line-height: 100px; border-radius: 50%; background: yellowgreen; background-image: linear-gradient(to right, transparent 50%, #655 0); color: transparent; text-align: center; } @keyframes spin { to { transform: rotate(.5turn); } } @keyframes bg { 50% { background: #655; } } .pie::before { content: ''; position: absolute; top: 0; left: 50%; width: 50%; height: 100%; border-radius: 0 100% 100% 0 / 50%; background-color: inherit; transform-origin: left; animation: spin 50s linear infinite, bg 100s step-end infinite; animation-play-state: paused; animation-delay: inherit; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.pie {
  position: relative;
  width: 100px;
  line-height: 100px;
  border-radius: 50%;
  background: yellowgreen;
  background-image: linear-gradient(to right, transparent 50%, #655 0);
  color: transparent;
  text-align: center;
}
 
@keyframes spin {
  to { transform: rotate(.5turn); }
}
@keyframes bg {
  50% { background: #655; }
}
 
.pie::before {
  content: '';
  position: absolute;
  top: 0; left: 50%;
  width: 50%; height: 100%;
  border-radius: 0 100% 100% 0 / 50%;
  background-color: inherit;
  transform-origin: left;
  animation: spin 50s linear infinite, bg 100s step-end infinite;
  animation-play-state: paused;
  animation-delay: inherit;
}

 

See the Pen qdvRMv by Airen (@airen) on CodePen.

一、概述

SVG 是一种基于 XML 语法的图像格式,全称是可缩放矢量图(Scalable Vector Graphics)。其他图像格式都是基于像素处理的,SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。

必威 37

SVG 文件可以直接插入网页,成为 DOM 的一部分,然后用 JavaScript 和 CSS 进行操作。

<!DOCTYPE html> <html> <head></head> <body> <svg id="mysvg" xmlns="" viewBox="0 0 800 600" preserveAspectRatio="xMidYMid meet" > <circle id="mycircle" cx="400" cy="300" r="50" /> <svg> </body> </html>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head></head>
<body>
<svg
  id="mysvg"
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 800 600"
  preserveAspectRatio="xMidYMid meet"
>
  <circle id="mycircle" cx="400" cy="300" r="50" />
<svg>
</body>
</html>

上面是 SVG 代码直接插入网页的例子。

SVG 代码也可以写在一个独立文件中,然后用<img><object><embed><iframe>等标签插入网页。

<img src="circle.svg"> <object id="object" data="circle.svg" type="image/svg+xml"></object> <embed id="embed" src="icon.svg" type="image/svg+xml"> <iframe id="iframe" src="icon.svg"></iframe>

1
2
3
4
<img src="circle.svg">
<object id="object" data="circle.svg" type="image/svg+xml"></object>
<embed id="embed" src="icon.svg" type="image/svg+xml">
<iframe id="iframe" src="icon.svg"></iframe>

CSS 也可以使用 SVG 文件。

CSS

.logo { background: url(icon.svg); }

1
2
3
.logo {
  background: url(icon.svg);
}

SVG 文件还可以转为 BASE64 编码,然后作为 Data URI 写入网页。

<img src="data:image/svg+xml;base64,[data]">

1
<img src="data:image/svg+xml;base64,[data]">

绝对坐标绘制指令

这组指令的参数代表的是绝对坐标。假设当前画笔所在的位置为(x0,y0),则下面的绝对坐标指令代表的含义如下所示:

必威 38

移动画笔指令 M,画直线指令:LHV,闭合指令 Z 都比较简单;下面重点看看绘制曲线的几个指令。

Step6、给眼睛添加动画

两只眼睛都是沿着圆弧运动 ,例如左眼,首先用一个路径来规定它的运动轨迹:

XHTML

<path id="eyeright" d="M 40 60 A 15 15 180 0 1 60 60" style="fill: none; stroke-width: 0;" />

1
<path id="eyeright"  d="M 40 60 A 15 15 180 0 1 60 60" style="fill: none; stroke-width: 0;" />

然后使用animateMotion来设置动画:

XHTML

<circle class="eye" cx="" cy="" r="2.5" style="fill: #fff;"> <animateMotion dur="0.8s" repeatCount="indefinite" keyPoints="0;0;1;1" keyTimes="0;0.3;0.9;1" calcMode="linear"> <mpath xlink:href="#eyeleft"/> </animateMotion> </circle>

1
2
3
4
5
6
7
8
9
10
<circle class="eye" cx="" cy="" r="2.5" style="fill: #fff;">
  <animateMotion
    dur="0.8s"
    repeatCount="indefinite"
    keyPoints="0;0;1;1"
    keyTimes="0;0.3;0.9;1"
    calcMode="linear">
    <mpath xlink:href="#eyeleft"/>
  </animateMotion>
</circle>
  • dur:动画的时间
  • repeatCount:重复次数
  • keyPoints:运动路径的关键点
  • timePoints:时间的关键点
  • calcMode:控制动画的运动速率的变化,discrete | linear | paced | spline四个属性可选
  • mpath:指定一个外部定义的路径

必威 39

2.13 <pattern>标签

<pattern>标签用于自定义一个形状,该形状可以被引用来平铺一个区域。

JavaScript

<svg width="500" height="500"> <defs> <pattern id="dots" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse"> <circle fill="#bee9e8" cx="50" cy="50" r="35" /> </pattern> </defs> <rect x="0" y="0" width="100%" height="100%" fill="url(#dots)" /> </svg>

1
2
3
4
5
6
7
8
<svg width="500" height="500">
  <defs>
    <pattern id="dots" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse">
      <circle fill="#bee9e8" cx="50" cy="50" r="35" />
    </pattern>
  </defs>
  <rect x="0" y="0" width="100%" height="100%" fill="url(#dots)" />
</svg>

上面代码中,<pattern>标签将一个圆形定义为dots模式。patternUnits="userSpaceOnUse"表示<pattern>的宽度和长度是实际的像素值。然后,指定这个模式去填充下面的矩形。

蒙版

SVG 中的蒙版有两种——剪裁cliping <clipPath> 与遮罩mask <mask>,都需要在 <defs> 中定义,然后通过对应的属性进行引用。

XHTML

<svg> <defs> <!-- 剪裁的定义 --> <clipPath id="cliping">...</clipPath> <!-- 遮罩的定义 --> <mask id="mask">...</mask> </defs>   <!-- 剪裁的引用 --> <circle clip-path="url(#cliping)"></circle> <!-- 遮罩的引用 --> <circle clip-path="url(#mask)"></circle> </svg>

1
2
3
4
5
6
7
8
9
10
11
12
13
<svg>
<defs>
<!-- 剪裁的定义 -->
<clipPath id="cliping">...</clipPath>
<!-- 遮罩的定义 -->
<mask id="mask">...</mask>
</defs>
 
<!-- 剪裁的引用 -->
<circle clip-path="url(#cliping)"></circle>
<!-- 遮罩的引用 -->
<circle clip-path="url(#mask)"></circle>
</svg>

注:以上代码为了直观体现两者的使用方法,已剔除其余不相干代码,不可直接运行。

剪裁与遮罩的区别在于,剪裁是按照定义的形状界限分明地进行图像的展示与隐藏:

而遮罩相较于剪裁,多了渐变显示图像的功能,只要在 <mask> 中包裹渐变的定义即可。遮罩的展示策略是:

越黑越透明,越白越不透明,而遮色片(注:即遮罩)只有黑到白的灰階分布,所以如果作為遮色片的顏色是灰階以外的顏色,都會被轉換為灰階。——引用来源《SVG 研究之路 (9) – Clipping and Masking》

因此遮罩的功能其实是包含剪裁的,当遮罩使用的是纯黑的图像时,功能等同于剪裁。

必威 40

虚线的描线动效结合剪裁或者遮罩即可以完成(Codepen):

XHTML

<svg width="300" height="100" viewBox="0 0 300 100"> <defs> <clipPath id="dash" class="dash"> <rect x="0" y="20" width="10" height="34"></rect> <rect x="20" y="20" width="10" height="34"></rect> <rect x="40" y="20" width="10" height="34"></rect> <rect x="60" y="20" width="10" height="34"></rect> <rect x="80" y="20" width="10" height="34"></rect> </clipPath> <mask id="mask-dash" class="mask_dash"> <rect x="0" y="20" width="10" height="34"></rect> <rect x="20" y="20" width="10" height="34"></rect> <rect x="40" y="20" width="10" height="34"></rect> <rect x="60" y="20" width="10" height="34"></rect> <rect x="80" y="20" width="10" height="34"></rect> </mask> </defs>   <g clip-path="url(#dash)"> <line class="line" x1="0" y1="28" x2="100" y2="28"></line> </g> <g mask="url(#mask-dash)"> <rect x="0" y="36" width="100" height="8" fill="#eee"></rect> <line class="line" x1="0" y1="40" x2="100" y2="40"></line> </g> </svg>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<svg width="300" height="100" viewBox="0 0 300 100">
<defs>
<clipPath id="dash" class="dash">
<rect x="0" y="20" width="10" height="34"></rect>
<rect x="20" y="20" width="10" height="34"></rect>
<rect x="40" y="20" width="10" height="34"></rect>
<rect x="60" y="20" width="10" height="34"></rect>
<rect x="80" y="20" width="10" height="34"></rect>
</clipPath>
<mask id="mask-dash" class="mask_dash">
<rect x="0" y="20" width="10" height="34"></rect>
<rect x="20" y="20" width="10" height="34"></rect>
<rect x="40" y="20" width="10" height="34"></rect>
<rect x="60" y="20" width="10" height="34"></rect>
<rect x="80" y="20" width="10" height="34"></rect>
</mask>
</defs>
 
<g clip-path="url(#dash)">
<line class="line" x1="0" y1="28" x2="100" y2="28"></line>
</g>
<g mask="url(#mask-dash)">
<rect x="0" y="36" width="100" height="8" fill="#eee"></rect>
<line class="line" x1="0" y1="40" x2="100" y2="40"></line>
</g>
</svg>

CSS

.mask_dash rect{ fill: #fff; } .line { stroke: #000; stroke-width: 8px; stroke-dasharray: 100; stroke-dashoffset: 100; transition: stroke-dashoffset ease-in .5s; } svg:hover .line { stroke-dashoffset: 0; }

1
2
3
4
5
6
7
8
9
10
11
12
13
.mask_dash rect{
fill: #fff;
}
.line {
stroke: #000;
stroke-width: 8px;
stroke-dasharray: 100;
stroke-dashoffset: 100;
transition: stroke-dashoffset ease-in .5s;
}
svg:hover .line {
stroke-dashoffset: 0;
}

注意到上方使用了遮罩的集合里多了一个方形图像,是因为遮罩对于图形尺寸的要求更加严苛,line 在它的眼里不是东西,不提供任何效果支持,但是一旦加个方形垫背,line 就被接受了[翻白眼]。所以涉及到切割的蒙版,请尽量使用 clipPath

TAG标签:
版权声明:本文由必威发布于必威-前端,转载请注明出处:效果图如下必威,   译文出处