[SVG动画] 制作一个中二的魔法阵 作者: Semesse 时间: 2018-09-09 分类: Javascript,千叶 # 效果图 [点击这里查看效果(anime.js版)(效果更酷炫)](https://codepen.io/semperia/pen/mGBVmV) [点击这里查看效果(css版)(只有旋转)](https://codepen.io/semperia/pen/RYLGqG) 代码其实都在里面了 # 制作过程 ## 准备工作 首先我们得准备一个好看的svg的魔法阵,虽然这种东西好难找的,我在谷歌上面也没找到几个。但是重要的是过程嘛。有的svg把所有内容糊成一团,像这样 整个图片就一个 path,这样样是没法把魔法阵拆成几个部分来画的(~~耐心好的话可以自己拆~~),我们需要的是把整个魔法阵分成多个 path 来绘制的,比如 demo 中使用的 svg ([来源:pixiv](https://www.pixiv.net/member_illust.php?mode=medium&illust_id=19282839) 画师:sharkpp 进行了一些改造) ```HTML ``` 可以看到每个path都有一个class,之后我们会用在 anime.js 中用 CSS 选择器对这些 path 进行变换。 ## 旋转的魔法阵 这一部分比较简单,CSS 动画也可以做到。原理就是 CSS3 的 transform: rotate()属性,然后对不同 class 使用不同的旋转方向和速度。 ```CSS @keyframes rotate-clockwise { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes rotate-counterclockwise { 0% { transform: rotate(360deg); } 100% { transform: rotate(0deg); } } .clockwise { animation: rotate-clockwise 5s linear 0s infinite; } .counterclockwise { animation: rotate-counterclockwise 5s linear 0s infinite; } .clockwise-long { animation: rotate-clockwise 20s linear 0s infinite; } .counterclockwise-long { animation: rotate-counterclockwise 30s linear 0s infinite; } path { transform-origin: center; } ``` 值得注意的是 transform-origin 属性,如果 transform-origin 不在圆心的话旋转起来就会鬼畜。 ```javascript anime({ targets: ".clockwise", rotate: ["0deg", "360deg"], loop: true, duration: 5000, easing: "linear", offset: 0 }) anime({ targets: '.counterclockwise', rotate: ['360deg','0deg'], loop: true, duration: 10000, easing: 'linear', offset: 0 }) anime({ targets: '.counterclockwise-long', rotate: ['360deg','0deg'], loop: true, duration: 10000, easing: 'linear', offset: 0 }) anime({ targets: '.clockwise-long', rotate: ['0deg','360deg'], loop: true, duration: 20000, easing: 'linear', offset: 0 }) anime({ targets: '.sc', rotate: ['0deg','360deg'], scale: [1.3,1.3], loop: true, duration: 5000, easing: 'linear', offset: 0 }) ``` anime.js 的操作方法类似,不过注意不建议用 timeline。每个 class 旋转的周期不一样,用 timeline 会造成周期短的旋转完停下来等待,除非强行按比例缩放 duration 和 totate 角度让所有图案的旋转周期相同(最小公倍数?)。 ## 魔法阵的出现动画 这个完全就发挥想象力了,可以变换的内容很多,比如 scale, rotate, translate, dasharray 甚至 rotate3D,怎么喜欢怎么来就行。这里就用 anime.js 了,毕竟 CSS 莫法控制动画完成后操作(魔法阵旋转) ```javascript var rotateloop = function(){ anime({ targets: ".clockwise", rotate: ["0deg", "360deg"], loop: true, duration: 5000, easing: "linear", offset: 0 }) //... //跟上面那段的代码一样,洗净备好,稍后再用 } ``` ```javascript $(document).ready(function(){ document.getElementById('demo').style.display = 'block' var timeline = anime.timeline() timeline.add({ targets: '.counterclockwise', strokeDasharray: { value: [(el) => `0 ${anime.setDashoffset(el)/8}`, (el) => `${anime.setDashoffset(el)/8} ${0}`], duration: 2000 }, // scale: [0,1], rotate: ['360deg','-360deg'], duration: 10000, easing: 'linear', offset: 0 }).add({ targets: '.scale0', scale: [0.001,1], duration: 3500, // easing: 'easeInOutSine', elasticity: 20, offset: 500 }).add({ targets: '.clockwise,.sc', scale: { value: [0.001, 1], duration: 2000, elasticity: 20 }, rotate: { value: ['-576deg','0deg'], duration: 8000, easing: 'linear' }, duration: 1000, offset: 2000 }).add({ targets: '.sc', scale: { value: [1, 1.3], duration: 1000 }, rotate: { value: ['-360deg','0deg'], duration: 5000 }, offset: 5000, easing: 'linear' }).add({ targets: '.scale1', scale: [0.001, 1], duration: 2000, easing: 'linear', offset: 5000 }).add({ targets: '.counterclockwise-long', scale: { value: [0.01, 1], duration: 1500, // elasticity: 200 easing: 'easeInOutSine' }, rotate: { value: ['180deg','0deg'], duration: 5000, easing: 'linear' }, offset: 5000 }).add({ targets: '.clockwise-long', scale: { value: [0.01, 1], duration: 2000, // elasticity: 100 easing: 'easeInOutSine' }, rotate: { value: ['-81deg','0deg'], duration: 4500, easing: 'linear' }, offset: 5500, complete: rotateloop }) }) ``` timeline 用起来确实是很爽的,只要 add 各种变换就行了 * rotate[X/Y/Z/3D] 旋转 * translate[X/Y/Z] 平移 * scale 缩放 * strokeDasharray 虚线实-虚样式 [参见 MDN](https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute/stroke-dasharray) * strokeDashoffset 虚线样式偏移 [参见 MDN](https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute/stroke-dashoffset) * 其实还有路径变形,填充,遮罩之类的高级玩法 不过由于 anime.js 自身的限制(倒不如说是 anime.js 不会合并 transform),同一个元素的 transform 不能分开到两个 anime 对象中(每个对象都是独立运行的),这样只会有后一个生效,例如 ```js anime({ targers: '#demo', rotate: '30deg' }) anime({ targers: '#demo', scale: 1.5 }) //或者 anime.timeline().add({ targers: '#demo', rotate: '30deg' }).add({ targers: '#demo', scale: 1.5 }) ``` 然后就是各种参数的设定了,要注意动画的连续性(比如转到 30° 突然变回 0° 开始转),这里我是算好了旋转的速度然后根据 duration 计算旋转角度。最后就是可以用一些缓动函数来让魔法阵显得不那么生硬。 ## 参考资料 [Anime.js Documentation](http://animejs.com/documentation/) [缓动函数速查表](https://easings.net/) [MDN SVG](https://developer.mozilla.org/zh-CN/docs/Web/SVG) 还有可以百度/谷歌到的各种 svg 动画的做法 如有问题,欢迎留言 / [Email me](mailto:Semperia@outclook.com) 标签: SVG