今晚是平安夜,提前给大家送份祝福。
SVG 是用 Illustrator 画的,苹果是手绘的(虽然是画的丑了点 0.0)。
按照惯例,看完效果之后我们来学习下它的实现原理。
思路分析
SVG 是网页上画矢量图的技术,有 line(线)、polyline(折线)、polygon(多边形)、react(矩形)、circle(圆形)、ellipsis(椭圆)等图形,也可以通过 path 来描述任意的形状。
- <svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
- <rect width="300" height="100"/>
- <path d="M250 150 L150 350 L350 350 Z" />
- </svg>
其中 M 是 moveTo,移动到某个位置开始画,L 是 lineTo,画一条直线,Z 是 closePath,完成绘制。当然,还可以绘制曲线等,api 和 canvas 里画图形的 api 差不多。
那我们需要用代码手画苹果和文字的 path 么?
不用,可以用 Illastrator 这种矢量绘图软件,它有钢笔、文字等各种绘图工具,用这些工具绘制完然后导出 SVG 就行。
SVG 可以设置两个方面的属性,一个是线条相关的,主要是 stroke,一个是填充相关的,主要是 fill。
stroke 相关的样式有 stroke-dasharray 来指定用虚线画。
比如 stroke-dasharray:10 20; 是虚线长度 10px,间隔 20px。
也可以用 stroke-dasharray 指定虚线的 offset,也就是偏移位置,往左偏移是正,往右是负。
比如 stroke-dashoffset: 1 就是往左偏移 1px。
注意这个 stroke-dashoffset,我们往左偏移全部的长度,然后再慢慢移动回原来位置,也就是 offset 由正数到 0,不就是描边的效果的呢?
这就是 SVG 描边动画的原理。
知道了这种动画要改变什么属性,那我们要用定时器自己去改变么?不用,可以用一些动画框架来修改属性值,比如 anime.js,它还支持设置时间函数,比如匀速、加速等。
理清了大概的原理,那我们来动手实现下吧。
代码实现
首先先把 SVG 准备好,我们用 Illastrator 来画:
使用文字工具写祝福的文字,然后选择“创建轮廓”,就会变成一个轮廓数据,然后选择导出 SVG 就行了。
苹果可以用钢笔手画,画完后点击“创建复合路径”,变成 path 的形式,然后导出 SVG:
画好了 SVG 之后,我们再实现动画效果:
- 最开始把 stroke-dashoffset 设置为 SVG 的长度,然后用动画的方式慢慢变为 0。
- 每一个 path 画完之后就用 fill 属性来填充颜色。
我们给每个 path 加上一个 class,然后来做动画。
使用 anime.js 来改变 stroke-dashoffset 实现描边的动画效果。
需要传入这些参数:
- 指定 targets,也就是添加动画效果的元素
- 指定 strokeDashOffset 属性值从 SVG 的长度慢慢变到 0。
- 指定 easing 时间函数,linear 是匀速
- 指定 duration 时间间隔
- 指定每个元素的执行动画的 delay 时间
- 还可以指定每个元素动画结束之后修改一些属性值
- const duration = 800;
- anime({
- targets: ".svg-path",
- strokeDashoffset: function(item) {
- const svgLength = anime.setDashoffset(item);
- return [svgLength, 0];
- },
- easing: "linear",
- duration,
- delay: function (item, index) {
- return duration * index;
- },
- update: function (anim) {
- const color = ['red', 'pink', 'purple','skyblue', 'blue','red', 'green','green'];
- for(let i = 1; i <= color.length; i++) {
- if (anim.currentTime >= duration * i) {
- document.querySelector(".path" + i).style.fill = color[i-1];
- }
- }
- },
- autoplay: true
- });
我们给每个元素设置了不同的动画 delay 时间,就是一个个做动画的效果。
每个元素执行结束之后,判断了下时间,如果是已经执行完动画的元素,就 fill 上颜色。
stokeDashOffset 的初始值是 SVG 的长度(向左偏移),然后慢慢变为 0(回到原点)。
这样,我们就实现了想要的描边动画的效果。
全部代码:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>平安夜快乐</title>
- <script src="https://unpkg.com/animejs@3.2.1/lib/anime.min.js"></script>
- <style>
- body {
- background-color: #000;
- }
- .box {
- width: 600px;
- height: 300px;
- text-align: center;
- margin: 0 auto;
- }
- svg {
- width: 600px;
- height: 300px;
- padding-top: 30px;
- }
- .svg-path {
- fill: #000;
- stroke: #fff;
- }
- </style>
- </head>
- <body>
- <div class="box">
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50.34 333.9">
- <path class="svg-path path1"
- d="M289.75,295.3v1.8a8.37,8.37,0,0,1-.24,2.1c-16.68,0-29.94-.36-39.78-1.2a7.41,7.41,0,0,1-.17-2,6.49,6.49,0,0,1,.29-2Q269.92,293.92,289.75,295.3Zm-29.58,22.92a9.45,9.45,0,0,1-3.54,2.22,50.86,50.86,0,0,1-5.82-8.58,9.62,9.62,0,0,1,3.61-2.16A98.3,98.3,0,0,1,260.17,318.22Zm26.76,8.7a6.52,6.52,0,0,1,.18,1.86,6,6,0,0,1-.18,1.86c-.6.24-3.78.36-9.48.36h-4.86c-.12,2.76-.3,5.22-.48,7.5a6.22,6.22,0,0,1-2,.24,4.7,4.7,0,0,1-1.68-.24c-.18-2.4-.3-4.86-.36-7.5-9.54,0-14.88-.12-15.95-.36a5.88,5.88,0,0,1-.25-2,5.49,5.49,0,0,1,.3-2c4.92-.12,10.2-.18,15.78-.18v-3.42q0-5.85.36-12.24a10.57,10.57,0,0,1,2-.24,9.05,9.05,0,0,1,2,.24q.36,6.48.36,11.88v3.72h4.74A79,79,0,0,1,286.93,326.92Zm5-15.12a49.35,49.35,0,0,1-6.66,9.54,4.71,4.71,0,0,1-3.06-1.8,46.39,46.39,0,0,1,6.18-9.84A6.85,6.85,0,0,1,291.91,311.8Z"
- transform="translate(-245.6 -293.92)" />
- <path class="svg-path path2"
- d="M254.11,372.4a129.35,129.35,0,0,1-.71,13,6.56,6.56,0,0,1-2.11.24,2.82,2.82,0,0,1-2-.48,88.15,88.15,0,0,1,.67-13,20.77,20.77,0,0,1,2.1-.12A5.79,5.79,0,0,1,254.11,372.4Zm34.68,16.86v2c-7.2.6-14.34,1-21.54,1.32-.9,2.52-1.74,5.1-2.58,7.62,4.08,1,8.16,1.8,12.3,2.64,1-2.52,1.92-4.92,2.88-7.08q1.8.54,3.78,1.26c-.66,2.28-1.38,4.56-2.22,6.72l5.46,1.08a16.5,16.5,0,0,1-1.08,3.66c-2-.36-4-.66-5.94-1-.6,1.26-1.14,2.52-1.74,3.78a8.53,8.53,0,0,1-3.54-1.74c.3-1,.6-2,1-2.88-5.76-1.14-11.46-2.46-17.16-4,1.14-3.42,2.28-6.66,3.48-9.78-3,.18-6,.3-8.87.36a21.4,21.4,0,0,1-.37-4c3.66-.24,7.32-.42,10.92-.66q1.62-4.23,3.42-8.1a31.59,31.59,0,0,1,4,1.32c-.78,2.16-1.56,4.32-2.28,6.48.6,0,1.2-.06,1.8-.12,5.88-.3,11.94-.54,18.06-.72A9.87,9.87,0,0,1,288.79,389.26ZM270.25,372.4c-.18-2-.24-4.08-.24-6.18.72-.06,1.5-.12,2.4-.24a19.25,19.25,0,0,1,2.16.12c0,2.16.06,4.26.06,6.42q8.64.18,17.1.54.36,5.4.36,10.26a4.82,4.82,0,0,1-1.8.24,14.21,14.21,0,0,1-2.1-.12c-.24-1.38-.48-3.66-.72-6.78-9.24,0-18.6-.24-28-.72a6.39,6.39,0,0,1-.24-2,8.5,8.5,0,0,1,.12-1.68C263,372.28,266.65,372.34,270.25,372.4Z"
- transform="translate(-245.6 -293.92)" />
- <path class="svg-path path3"
- d="M263.41,455.92c-5.46,2.88-10.74,5.46-15.89,7.68a6.9,6.9,0,0,1-1.92-3.18,175.82,175.82,0,0,1,15.83-8.34A16.66,16.66,0,0,1,263.41,455.92Zm9.66-15.9h7.38a87,87,0,0,1,9,.42,6.32,6.32,0,0,1,.18,1.8,5.26,5.26,0,0,1-.18,1.8c-.6.24-3.6.3-9,.3-18.06,0-27.9-.06-29.51-.3a5.28,5.28,0,0,1-.25-1.92,4.33,4.33,0,0,1,.31-1.86l17.75-.18a30.6,30.6,0,0,1-.48-4q1.8-.27,4-.54C272.59,437,272.83,438.52,273.07,440Zm-13.26,35.34a9.85,9.85,0,0,1-3.66.66,56.49,56.49,0,0,1-3-13,16.28,16.28,0,0,1,3.48-.72A65.47,65.47,0,0,1,259.81,475.36Zm8.1-10.32a8.29,8.29,0,0,1-3.18-2.22,65.29,65.29,0,0,1,11.4-13.26,11.4,11.4,0,0,1,2.7,2.1c-.48.72-1,1.5-1.56,2.22,4.5,1.32,9.3,2.82,14.46,4.5-1.68,4.8-3.54,9.42-5.46,13.86-.78,1.86-1.5,3.48-2.1,4.86,2.52,1,5,2,7.38,3a20.4,20.4,0,0,1-1.38,3.42c-2.64-.78-5.16-1.62-7.68-2.52a26.33,26.33,0,0,1-2,3.9,10.66,10.66,0,0,1-3.84-1.74c.48-1.26,1-2.52,1.56-3.72-3.48-1.26-7-2.64-10.38-4.2a13.37,13.37,0,0,1,1.38-3.78c3.6,1.32,7.2,2.64,10.74,4,2.1-4.92,4.14-9.78,6.18-14.58-4.08-1.14-7.86-2.34-11.34-3.66C272.77,459.58,270.49,462.22,267.91,465Zm12.54,1.14a14.75,14.75,0,0,1-1.5,3.3,19,19,0,0,1-6.3-2.76,17.21,17.21,0,0,1,1.56-3.48C276.31,464.14,278.41,465.1,280.45,466.18Z"
- transform="translate(-245.6 -293.92)" />
- <path class="svg-path path4"
- d="M251.71,539a21.77,21.77,0,0,1-3.9.36c-.77-5.52-1.25-11-1.49-16.62a22,22,0,0,1,4.08-.36C250.94,528,251.35,533.62,251.71,539Zm7.8-14a64.55,64.55,0,0,1,6.54,1.08,20.77,20.77,0,0,1-.42,3.6,22.77,22.77,0,0,1-5.82-.66c.36,5.64.6,11.1.66,16.38a4.87,4.87,0,0,1-1.74.24,6.14,6.14,0,0,1-1.68-.12A261.61,261.61,0,0,1,255,515.8a11,11,0,0,1,1.92-.24h1.79C259,518.8,259.27,521.92,259.51,525Zm21.18-13.5a9.67,9.67,0,0,1,3.84,1.26c-.54,1.86-1.26,4.44-2.16,7.68,3,.06,6.18.18,9.66.36.36,3.24.72,7.5,1.14,12.9a13.74,13.74,0,0,1-4,.54c-.6-3.84-1-7.08-1.38-9.84-2.4,0-4.62-.06-6.6-.06-1,3.24-2,7-3.18,11.28h9.42a73.75,73.75,0,0,1,8.28.42,5.65,5.65,0,0,1,.18,1.68,4.47,4.47,0,0,1-.18,1.62c-.54.24-3.3.3-8.28.3H276.91c-1.08,3.84-2.28,8-3.48,12.42a11.64,11.64,0,0,1-3.6-1.26c.66-3.54,1.5-7.26,2.46-11.22a77,77,0,0,1-7.92-.24,7,7,0,0,1-.18-1.74,4.26,4.26,0,0,1,.3-1.74l8.76-.18c1-3.6,2.1-7.44,3.3-11.46-3-.06-5.52-.18-7.68-.3a14.37,14.37,0,0,1-.3-3.3c3.24-.12,6.3-.18,9.18-.24C278.65,517.48,279.61,514.48,280.69,511.48Zm11.22,36.06a9.05,9.05,0,0,1-.9,1.68,11.09,11.09,0,0,1-1.14,1.62,28.41,28.41,0,0,1-9.18-4.5,9.93,9.93,0,0,1,2.1-3.42C285.73,544.3,288.79,545.86,291.91,547.54Z"
- transform="translate(-245.6 -293.92)" />
- <path class="svg-path path5"
- d="M255.19,591.4a66.44,66.44,0,0,1-.72,9.42c2.1.6,6.18,1,12.3,1.08-.42-3.72-.84-7.38-1.2-11.1a18.16,18.16,0,0,1,4.2-.6c.6,4,1.14,7.86,1.62,11.76,9.12,0,15.36-.3,18.6-.9a17.86,17.86,0,0,1,.48,4c-2.88,1.08-9,1.62-18.48,1.74q1.08,9.72,1.62,19.62a31.7,31.7,0,0,1-10.44,1.44,7.53,7.53,0,0,1-.84-3.42,44.52,44.52,0,0,1,6.84-.9c-.66-5.58-1.26-11.16-1.86-16.74-8.64-.12-14.52-.66-17.69-1.62a94.48,94.48,0,0,1,1-13.86,19.81,19.81,0,0,1,2.35-.12C254.42,591.34,255.19,591.4,255.19,591.4Zm32-7.92a14.29,14.29,0,0,1,.12,2q-18.54,2.61-36.77,3.9a4.47,4.47,0,0,1-.48-2.1v-1.8c12.35-1.56,24.59-2.76,36.83-3.72A6.24,6.24,0,0,1,287.17,583.48Zm-28.08,31c-1,3.12-2,6.6-3.36,10.38a8.26,8.26,0,0,1-4.14-1.32A55.78,55.78,0,0,1,255,613,20.2,20.2,0,0,1,259.09,614.5Zm29.16,2.28c.72,1.44,1.38,2.82,1.86,4.08a9.78,9.78,0,0,1-4,1.74,39.45,39.45,0,0,1-4.38-8.7,22.14,22.14,0,0,1,4.08-1.62C286.69,613.84,287.53,615.34,288.25,616.78Z"
- transform="translate(-245.6 -293.92)" />
- </svg>
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 219.1 246.81">
- <path class="svg-path path6"
- d="M326.34,316.17h32.53l27.71,25.3,10.84,36.14V421l-9.64,37.35L376,486.05l-31.54,14.46-28.92-6-15.66-10.84-5.37,4.7-13.91,12.17H262.48l-30.12-10.85L201,471.59,186.58,433l-7.23-51.81,34.94-39.76,85.54,9.64Z"
- transform="translate(-178.82 -254.22)" />
- <path class="svg-path path7"
- d="M243.2,296.89l-4.81-14.46L248,254.72h14.46l23.5,12,23.49,18.07v24.1l-9.64,42.17L262.48,321Z"
- transform="translate(-178.82 -254.22)" />
- <path class="svg-path path8" d="M248,254.72l18.29,24.63,23.87,40.7,9.65,31.06"
- transform="translate(-178.82 -254.22)" />
- </svg>
- </div>
- <script>
- const duration = 800;
- anime({
- targets: ".svg-path",
- strokeDashoffset: function(item) {
- const svgLength = anime.setDashoffset(item);
- return [svgLength, 0];
- },
- easing: "linear",
- duration,
- delay: function (item, index) {
- return duration * index;
- },
- update: function (anim) {
- const color = ['red', 'pink', 'purple','skyblue', 'blue','red', 'green','green'];
- for(let i = 1; i <= color.length; i++) {
- if (anim.currentTime >= duration * i) {
- document.querySelector(".path" + i).style.fill = color[i-1];
- }
- }
- },
- autoplay: true
- });
- </script>
- </body>
- </html>
总结
SVG 是网页里画矢量图的技术,可以用 rect、circle、line 等画一些具体的图形,也可以用 path 来画更复杂的图形。
SVG path 的 api 和 canvas 里的 path 差不多,比如 M 是 moveTo,L 是 lineTo,Z 是 closePath。
复杂图形可以用矢量图绘制软件 Illuastrator 来绘制,之后导出为 SVG。
SVG 可以设置的属性主要有 stroke 指定线条样式,fill 指定填充样式。
其中 stroke-dasharray 是指定虚线长度,stroke-dashoffset 是指定虚线偏移,正数向左,负数向右。
通过 stroke-dashoffset 的值的变化,就能实现描边的效果,从 SVG 的整体长度慢慢变化到 0。
属性值的修改可以用动画框架 anime.js,它支持定时修改元素的属性值来做动画,并且支持匀速、加速等时间函数。
文中的那个动画,我们指定每个 path 的 delay 时间,每个 path 绘制完之后设置 fill 属性即可。
SVG 的描边动画还是挺不错的效果,可以用在很多地方。实现原理也不难,就是一个 offset 属性值的修改。大家很快就能学会。
最后,再次祝大家平安夜快乐呀,加上今天是周五,double 的快乐~
原文链接:https://mp.weixin.qq.com/s/kO5X6QltSz2BXp_YmWfxLw