程序化微笑 - 使用纯 JavaScript 动画 SVG
如何用纯 JavaScript 动画 SVG
我最近需要生成一个简单的面部图像,并且能够将面部表情从快乐调整到悲伤。
(为什么我要这样做是一个很长的故事!)
这给了我一个机会来玩 SVG,这是我已经有一段时间没有做过的事情,并且一直希望我能花更多时间去做。你可以在下面看到结果,移动滑块可以看到微笑动画
查看 CodePen 上 Dave Kerr (@dwmkerr) 的作品 SVG Smile。CodePen.
源码: github.com/dwmkerr/svg-smile/
CodePen: codepen.io/dwmkerr/pen/ejejeX
工作原理 - 几何学
这是一个很容易实现的效果,诀窍在于弄清楚微笑的几何形状是如何工作的
黑点是微笑的起点和终点,红点是贝塞尔曲线的控制点。这意味着我们可以通过简单地插值上面显示的两个极端值的锚点和控制点的位置来缩放微笑到皱眉。
面部本身(不包含样式)看起来像这样
<svg viewbox="0 0 120 120">
<g transform='translate(60 60)'>
<!-- First the main circle for the face. -->
<circle
cx="0"
cy="0"
r="50" />
<!-- Then the left eye... -->
<circle
cx="-20"
cy="-10"
r="5" />
<!-- Then the right... -->
<circle
cx="20"
cy="-10"
r="5" />
<!-- The smile bezier curve. -->
<g transform="translate(0, 25)">
<path
d="M-20,-10 C-20,10 20,10 20,-10" />
</g>
</g>
</svg>
这里的诀窍实际上是使用任何适合你的坐标系。我首先定义一个视区给我一些空间,转换原点,然后将面部的主要圆圈正好放在(0, 0)
的中间。
如果我们也移动微笑的原点,插值微笑控制点的代码会更容易。这种技术非常适用于 SVG(或任何计算机图形),操作和变换以获得适合你的坐标系,并使其更容易理解正在发生的事情。
工作原理 - 动画
我以前没有做过 SVG 动画。在研究如何做这件事时,绝大多数提示、博客、文章等等都建议使用库(常见的建议是vivus、snap.svg和svg.js)。
毫无疑问,当你了解 SVG 的使用方法时,使用库是一个巨大的加速器,并且可以节省样板代码。但是,如果你不知道库在做什么,它在封装什么,或者它为你解决了什么问题,你可能会错过一些基础知识。
如果你知道要解决什么问题,使用库是很棒的。但如果你不知道,你最终可能永远学不会。我不知道用纯 SVG API 来做这件事是否具有挑战性,并且绝对想手动操作。
经过一些实验,我能够编写可以将微笑移动到皱眉的标记
<g transform="translate(0, 25)">
<path id="smilepath" d="M-20,-10 C-20,10 20,10 20,-10">
<animate
attributeName="d" attributeType="XML"
to="M-20,10 C-20,-10 20,-10 20,10" dur="3s"
repeatCount="indefinite"
fill="freeze"
/>
</path>
</g>
我们已经看到了几何形状,我们在这里所做的只是交换每个锚点及其关联控制点的位置。诀窍只是确保我们正确获取animate
元素的属性。
完成此操作后,最后一步就是使其全部编程化。根据从 0(悲伤)到 1(快乐)的比例生成路径几何形状的代码在线上,但有趣的是如何运行动画
// note that 'scale' is 0->1 (sad->happy)
const points = writeSmilePoints(smilePoints(scale));
const svg = document.getElementById('svg');
const smilePath = document.getElementById('smilepath');
const animate = document.createElementNS(svg.namespaceURI, 'animate');
animate.setAttribute('attributeName','d');
animate.setAttribute('attributeType','XML');
animate.setAttribute('to',points);
animate.setAttribute('dur','0.3s');
animate.setAttribute('repeatCount','1');
animate.setAttribute('fill','freeze');
smilePath.appendChild(animate);
animate.beginElement();
这没什么特别的。大部分代码只是为animate
标签设置属性。然后,我们将它作为子元素添加到路径中,并调用beginElement
来启动动画。
面部以类似的方式着色。在 JavaScript 中在快乐的辛普森黄色和愤怒的红色之间进行插值,然后设置一个animate
元素来定位相应圆形的fill
。
总结
玩图形很有趣!这只是对 SVG 功能的最基本了解。用于动画的 JavaScript 非常简单(尽管我理解浏览器不一致等等意味着某个时候库可能有用)。
代码可在 GitHub 上获取 github.com/dwmkerr/svg-smile 或 CodePen。