The Red Heart
今天在网上闲逛的时候看到了这个漂亮的心形曲线特效,动感十足。于是动手分析了一下它的实现方式,记于此文。
See the Pen The Red Heart by Ninja Lau (@mutoo) on CodePen.
记得 Make things with math 的作者 Steven Wittens 曾经说过:任何看起来复杂的函数图形,都是由各种简单函数组成的。这个心形曲线特效也不例外。
¶0x01 Heart Curve
Wolfram MathWorld 的 Heart Curve 页面记载了几种心形曲线的公式:
其中第六个参数方程非常符合我们的案例。代码中的 heartPosition 函数正是出于此:
var heartPosition = function (rad) {
return [16 * Math.pow(Math.sin(rad), 3), -(15 * Math.cos(rad) - 5 * Math.cos(2 * rad) - 2 * Math.cos(3 * rad) - Math.cos(4 * rad))];
};
该函数传入一个由原点发出的射线与x轴的夹角(弧度),返回一组坐标 [x, y] 。由于 canvas 中坐标系的 Y 轴是由上而下的,所以公式中的 Y 坐标需要取反。
¶0x02 Dotted Heart Curve
通过该函数绕原点一周,我们能得到一个单位长度的心形。从 0 rad 开始,并以设定好的角度 dr
步进,将心形曲线上的一些采样点保存下来:
var pointsOrigin = [];
var dr = 0.1, i;
for (i = 0; i < Math.PI * 2; i += dr)
pointsOrigin.push(heartPosition(i));
为了让心形更有层次感,我们可以参加一个简单的缩放函数,并创建多个心形:
var scaleAndTranslate = function (pos, sx, sy) {
return [pos[0] * sx, pos[1] * sy];
};
for (i = 0; i < Math.PI * 2; i += dr)
pointsOrigin.push(scaleAndTranslate(heartPosition(i), 2, 2));
for (i = 0; i < Math.PI * 2; i += dr)
pointsOrigin.push(scaleAndTranslate(heartPosition(i), 3, 3));
¶0x03 Particles
现在我们有了三个不同大小的心形曲线上的采样点了。为了让这些点变得生动一点,接下来需要制造一些粒子在其上面运动。这些粒子遵循下面的运动规则:
- 从采样点中随机选取一些点为作目标点;
- 使用简单的物理规则向目标点移动,例如:速度、摩擦力等;
- 当粒子接近目标点后,会根据概率重新选择目标点。其中有比较大的概念选择临近的采样点,这样可以让心形曲线保持完整的流动性。
¶0x04 Particle Tracking
粒子运动形单影支,如果能加上轨迹效果就更好了。可以在粒子初始化时在相同位置创建若干个轨迹粒子。轨迹粒子的运动可以采用缓动公式,逐渐向前一个轨迹粒子靠拢即可。
关于粒子运动的算法,这里不细说。想深入研究的话,强烈推荐 Foundation ActionScript 3.0 Animation: Making Things Move 一书,里面的例子简单易懂。
¶0x05 Heart Beating
既然是个心,那么如何让它跳动呢。首先我们需要一个函数,能对采样点进行缩放:
var targetPoints = [];
var pulse = function (kx, ky) {
for (i = 0; i < pointsOrigin.length; i++) {
targetPoints[i] = [];
targetPoints[i][0] = kx * pointsOrigin[i][0];
targetPoints[i][1] = ky * pointsOrigin[i][1];
}
};
然后周期性地改变缩放系数:
var time = 0;
var loop = function () {
var k = 0.5 + 0.5 * Math.cos(time);
pulse(k, k);
time += ((Math.sin(time)) < 0 ? 9 : (k > 0.8) ? .2 : 1) * 0.04;
/* render particles */
window.requestAnimationFrame(loop, canvas);
};
有趣的是,这里 time 的步进并不是均速的,而是一组分段函数,为了是模拟心跳的那种节奏感。这里用了很多魔数(magic numbers),显然都是慢慢微调出来的。
¶0x06 Color
红心的色彩是通过 HSLA 色彩空间随机选取不同饱和度和明度的 30% 透明红色,以此来表现出丰富的色泽,虽然只有红色,但是能够给人很炫丽的感觉。