先看看效果
实现原理
1.动画中每颗心都有自己的透明度,位移,角度,缩放的变化动画,针对这些动画,可以分别调用canvas的相关API:
1 2 3 4 5
| ctx.translate(this.x, this.y); ctx.rotate(this.angle); ctx.scale(this.scale, this.scale); ctx.globalAlpha = this.opacity;
|
2.在有大于1颗心的情况时,针对每颗心,在实现角度,缩放时必须要保存上一次canvas的状态,改变完成之后在恢复:
1 2 3 4 5 6 7
| ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.angle); ctx.scale(this.scale, this.scale); ctx.globalAlpha = this.opacity; ctx.restore();
|
3.计算心位移路径(3次被塞尔曲线)
公式:
转换成js代码计算心的x,y方向上的位移:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
function getBezierLine(heart){ var obj = heart.bezierPoint; var p0 = obj.p0; var p1 = obj.p1; var p2 = obj.p2; var p3 = obj.p3; var t = heart.bezierDis; var cx = 3 * (p1.x - p0.x), bx = 3 * (p2.x - p1.x) - cx, ax = p3.x - p0.x - cx - bx,
cy = 3 * (p1.y - p0.y), by = 3 * (p2.y - p1.y) - cy, ay = p3.y - p0.y - cy - by,
xt = ax * (t * t * t) + bx * (t * t) + cx * t + p0.x, yt = ay * (t * t * t) + by * (t * t) + cy * t + p0.y;
heart.bezierDis += heart.speed;
return { xt: xt, yt: yt } }
|
路径如图所示:
根据曲线路径方法可以实现心的运动轨迹:
4.针对每一帧需要动态修改,心的位移,角度,和缩放:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
function getFScale(heart){ let _scale = heart.scale;
let dis = heart.orignY - heart.y; _scale = (dis / heart.scaleDis);
if (dis >= heart.scaleDis) { _scale = 1; }
return _scale; }
|
5.计算心角度变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
function rangeAngle(heart) { let _angle = heart.angle; if(_angle >= heart.angelEnd) { heart.angleLeft = false; } else if (_angle <= heart.angelBegin){ heart.angleLeft = true; } if (heart.angleLeft) { _angle = _angle + 1; } else { _angle = _angle - 1; } return _angle; }
|
组件化
整个组件代码不超过200行,相较于使用css3实现更加轻便,dom少,性能更好。
组件地址:https://github.com/lvming6816077/like-heart