用SVG和Vanilla JS框架创建一个“星形变心形”的动画效果

我写的这篇文章中, 讲述了如何用Vanilla JavaScript使动画顺滑的从一种状态过渡到另一种。最好先看下那篇文章,因为在这篇文章中我们要用到一些那篇文章中讲过的内容。例如例子的演示、各种时间函数的公式、当从结束状态过渡到初始状态时不使时间函数倒转过来。都在那篇文章中做了详细讲解。

在最后的例子中,通过改变绘制嘴形的path的属性d,我们得到了从悲伤的嘴变高兴的嘴的效果。

更高水平的控制路径数据能够带给我们更有趣的效果,例如星形变心形。

这是我们要实现的星形变心形的动画效果。

思路

它们都是由五个三次贝塞尔曲线构成。下边的互动演示展示了每条曲线以及这些曲线相连接的点。点击任意曲线或连接点可以看到两个图形的曲线是如何相对应的。

可以看出所有曲线都是由三次贝塞尔曲线创建的。即使其中一些曲线的两个控制点重叠了。

构成星形和心形的形状都是极简且不符合实际的。但它们可以做到。

初始代码

从表情动画的例子中可以看出, 我通常选择用 Pug(译:即Jade,一种模版引擎) 生成这类形状。但在这里,由于生成的路径数据还将由JavaScript处理过渡效果。包括计算坐标以及将这些坐标放入属性d 。所以使用JavaScript来做所有的这些是最好的选择。

这意味着我们不必写很多标签:

<svg>
    <path id='shape'/>
</svg>

JavaScript中,我们首先获得元素 svg 和元素 pathpath 是那个星形变心形再变回星形的形状。然后,我们给元素 svg 设置viewBox属性,使得 SVG 沿两个轴的尺寸相等,并且坐标轴的原点(0,0)在 SVG 正中间。这意味着,当viewBox的尺寸值为D 时,它的左上角坐标为(-.5*D,-.5*D)。最后,这个也很重要,就是创建一个对象来存储过渡的初始和最终状态,以及一个将我们想要的值设置给 SVG 图形属性的方法。

const _SVG = document.querySelector('svg'), 
    _SHAPE = document.getElementById('shape'), 
    D = 1000, 
    O = { ini: {}, fin: {}, afn: {} };

(function init() {
    _SVG.setAttribute('viewBox', [-.5*D, -.5*D, D, D].join(' '));
})();

现在我们把这事解决了,可以开始更有趣的部分了!

图形的几何绘制

我们用终点和控制点的初始坐标来绘制星形,用它们的最终坐标来绘制心形。 每个坐标的过渡范围是它的初始值与最终值之间的差值。在这个例子中,当星形向心形转换时,我们会转动(rotate)它,因为我们想让星形的角朝上。我们还会改变填充(fill),从金色的星形变成深红色的心形。

那么,我们怎么能获得这两个图形的终点和控制点的坐标呢?

星形

在星形的例子中,我们先从一个正五角星形开始。我们的曲线(译:构成星形每个角的曲线)终点落在正五角星形边的交叉点上,我们把正五角星形的顶点作为控制点。

五个三次贝塞尔曲线的终点和控制点用黄点标识在了正五角星形的顶点和边的交叉点上(Live

直接给定正五角星形外接圆的半径(或者直径)就可以获得五角星形的顶点。也就是我们给 SVG 的viewBox 设定的尺寸(简单起见,在这种情况下我们不考虑高填密)。但是如何获得他们的交叉点呢?

首先,我们先看下边的说明图。注意图中正五角星形中间高亮标注的小五边形。小五边形的顶点与正五角星形边的交叉点是重合的。这个小五边形显然是个正五边形(译:五个边的长度相等)。这个小正五边形的内切圆和内径跟正五角星形的是同一个。

正五角星形和内部的正五边形的内切圆是同一个 (Live)。

因此,如果我们计算出正五角星形的内径,那么也就获得了正五边形的内径。这个内径和圆心角 一起对应正五边形的边。根据这个我们就可以获得正五边形的外接圆半径 。这样就可以倒推出正五边形顶点的坐标。这些点正是正五角星形边的交叉点,也就是星形五个三次贝塞尔曲线的终点。

我们的正五角星形可以用拓扑符号 {5/2}来表示。也就是说,正五角星形有5个顶点。这5个顶点均匀分布在它的外接圆上,间隔是 360°/5 = 72°。我们从第一个点开始,跳过紧挨着的下一个点,连接到紧挨着的第二个点(这就是符号{5/2}2的含义;1 代表的意思是连接到第一个点,不跳过任何点,构成一个五边形)。照这样一直连接,就可以画出正五角星形了。

在下边的演示中,点击五边形或者五角星形按钮,查看它们是怎样被绘制的。

这样,我们得到正五角星形的边所对应的的圆心角是正五边形的边所对应的圆心角的二倍。那么正五边形是1 * (360°/5) = 1 * 72° = 72° (或者1 * (2 * π / 5)弧度),那正五角星形就是2 * (360° / 5) = 2 * 72° = 144°2 * (2 * π / 5)弧度)。通常,一个用拓扑符号表示为{p,q}的正多边形,它的一个边所对应的圆心角就是 q * (360° / p)q * (2 * π / p) 弧度)。

OxyV072°144°216°288°

正多边形的一条边所对应的圆心角:正五角星形(左,144°)vs 正五边形(右,``72°`)(Live)。

已知正五角星形外接圆半径,也就是的viewBox尺寸。那么,已知直角三角形斜边的长(即正五角星形外接圆的半径)和锐角的度数(正五角星形一条边所对应的角度的一半),这意味着我们可以算出正五角星形的内径(这个内径与正五角星形内部的小正五边形的内径相等)。

72°144°xOMyV0V1

通过直角,可以计算出正五角星形的内径长。这个直角的斜边等于正五角星形外接圆半径,其中一个锐角的角度等于正五角星形一条边所对应的角度的一半 (Live)。

圆心角一半的余弦等于五角星形的内径比外接圆半径。就可以得出,五角星形的内径等于外接圆半径乘以这个余弦值。

现在我们得到了正五角星形内部小正五边形的内接圆半径,我们就可以计算出这个正五边形的外接圆半径了。还是通过一个小直角来计算。这个直角的斜边等于正五边形外接圆半径。一个锐角等于正五边形一条边所对应的圆心角的一半。这个锐角的一条边是这个圆心角的中直线,这个中直线是正五边形的外接圆半径。

下边的说明图中高亮标注了一个直角三角形,它是由正五边形的一条外接圆半径、内接圆半径、一个圆心角的一半构成的。如果我们已知内接圆半径和正五边形一条边所对应的圆心角,这个圆心角的一半也就是两条外接圆半径的夹角的话。用这个直角三角形我们可以计算出外接圆半径的长。

剩余80%内容付费后可查看
* 请输入阅读码(忘记阅读码?

如需转载,烦请注明出处:https://www.w3cplus.com/svg/creating-a-star-to-heart-animation-with-svg-and-vanilla-javascript.html

如果文章中有不对之处,烦请各位大神拍正。如果你觉得这篇文章对你有所帮助,打个赏,让我有更大的动力去创作。(^_^)。看完了?还不过瘾?点击向作者提问!

赏杯咖啡,鼓励他创作更多优质内容!
返回顶部