补间动画
补间动画是指在Flash的时间帧面板上,在一个关键帧上放置一个元件,然后在另一个关键帧
改变这个元件的大小、颜色、位置、透明度等,Flash 将自动根据二者**之间的**帧的值创建的**动画**。
tween.js
Tween.js是一个包含各种经典动画算法的JS资源,,AS中甚至有专门的Tween类。`Quad`, `Cubic`等等
都是经典的动画运动算法名称,完整列表如下:
- Linear:线性匀速运动效果;
- Quadratic:二次方的缓动(t^2);
- Cubic:三次方的缓动(t^3);
- Quartic:四次方的缓动(t^4);
- Quintic:五次方的缓动(t^5);
- Sinusoidal:正弦曲线的缓动(sin(t));
- Exponential:指数曲线的缓动(2^t);
- Circular:圆形曲线的缓动(sqrt(1-t^2));
- Elastic:指数衰减的正弦曲线缓动;
- Back:超过范围的三次方缓动((s+1)t^3 – st^2);
- Bounce:指数衰减的反弹缓动。
每个效果都分三个缓动方式,分别是:
- easeIn:从0开始加速的缓动,也就是先慢后快;
- easeOut:减速到0的缓动,也就是先快后慢;
- easeInOut:前半段从0开始加速,后半段减速到0的缓动。
所有的这些缓动算法都离不开下面4个参数,
t
,b
,c
,d
,含义如下:
/*
* t: current time(当前时间);
* b: beginning value(初始值);
* c: change in value(变化量);
* d: duration(持续时间)。
*/
只看上面字面意思其实不好理解,我们套用最简单的匀速直线运动来解释下:
我们都知道,匀速直线运动的公式为
s1 = s2 + vt
结合缓动算法的参数,我们来尝试定义一下Tween.Linear,其实就是匀速直线运动的公式。
s1 = s2 + vt
v = s / t = c / d
s1 = b + (c / d) * t
s1 = b + c*t / d
Tween.Linear = function(t, b, c, d) {
return c*t/d + b;
}
如果我们知道一个元素在每个时刻的位置,那么我们就可以利用requestAnimationFrame实现动画效果
var t = 0, b = 0, c = 1000, d = 10;
var step = function () {
// value就是当前的位置值
// 实现右移动画
DOM.style.right = value + 'px'
var value = Tween.Linear(t, b, c, d);
t = +new Date()
if (t <= d) {
// 继续运动
requestAnimationFrame(step);
}
};
点赞
1、初始化时,创建一组点赞要跳跃的图片元素,存储起来,但是不挂载,同时创建对应个数的点赞动效对象(容器),保存起来待命
2、当用户触发点赞动作时,开始将点赞要跳跃的图片元素,依次塞进点赞动效容器中,在这个过程中,要判断容器中是否已经有正在执行的跳跃元素,寻找到空的动效容器进行填充并执行动画效果。
3、一般情况下需要跳跃的元素,是一组一组进行填充的,假如我们点赞的速度快了,那么就会出现动效容器不够用的情况,这时候我们就需要动态的添加动效容器。
import Partical from './partical.js'
class Boom {
constructor() {}
boom() {
let boomNums = 0
// unAnimateList为未填充的动效元素
for (const partical of unAnimateList) {
// this.particalNumbers为一次点赞能触发最大的图片元素
if (boomNums >= this.allowNumbers) return
// 在执行填充前再次判断动效容器的状态
if (partical.animating) {
continue
}
boomNums++
const r = Math.random()
// 将动效容器挂载到点赞dom处
partical.renderIn(this.con)
// childList为图片元素列表,将图片元素填充到动效容器
partical.insertChild(childList[Math.floor(r * childListLength)].cloneNode(true))
// 执行动效
partical.animate({
deg: (r * spread + rotate) % 360,
pow: r * power + 1,
delay: r * delayRange
})
}
// 如果动效容器不够用,则添加动效容器
if (boomNums < this.particalNumbers) {
this.createParticals(this.particalNumbers - boomNums)
}
}
}
动效
1、动效是利用补间动画实现的,使用tween.js来计算动效的轨迹。
2、一般情况下点赞动画的轨迹,在x轴方向为匀速直线运动,在y方向上半段为竖直上抛,下半段为自由落体,确定好缓动方式后,再来确定缓动轨迹,点赞大多为抛物线。
3、利用requestAnimationFrame计算出
let r = Math.random()
this.targetZ = Math.round(pow * pow) * (r < 0.5 ? -1 : 1)
this.targetY = Math.round(pow * Math.sin(deg * DEG) * POWER)
this.targetX = Math.round(pow * Math.cos(deg * DEG) * POWER) * (r + 1)
moveX(currentDuration) {
return Math.tween.Linear(currentDuration, 0, this.targetX,Duration) * 2
}
moveY(currentDuration) {
let direction = this.direction
if (direction === 'UP') { // 抛物线
// 如果是上抛运动
if (currentDuration < Duration / 2) { // 上半段
// 上抛过程
return Quad.easeOut(currentDuration, 0, this.targetY + G, Duration / 2)
}
// 下降过程
return this.targetY + G - Quad.easeIn(currentDuration - Duration / 2, 0, this.targetY / 2, Duration / 2)
}
return Quad.easeIn(currentDuration, 0, this.targetY, Duration) // 上升点赞
}
scale(currentDuration) {
return Quad.easeOut(currentDuration, 1, this.scaleNum, Duration)
}
opacity(currentDuration) {
return Quad.easeIn(currentDuration, 1, -1, Duration)
}
let animate = () => {
let timeGap = +new Date() - StartTimeAfterDelay
if (timeGap >= 0) {
if (timeGap > Duration) { // Duration为动效执行的周期
this.emitEndCB()
return
}
// 动效涉及x方向,y方向,大小,透明度
this.dom.style.cssText += `;will-change:transform;-webkit-transform:translate3d(${this.moveX(
timeGap
)}vh,${this.moveY(timeGap)}vh,0) scale(${this.scale(timeGap)});opacity:${this.opacity(timeGap)};`
}
requestAnimationFrame(animate)
}