WebAnimation 与 FLIP 技术
Web Animations API
类似于 CSS in JS 的 animation 实现.
基本使用
定义关键帧
使用对象数组的模式定义关键帧
const aliceTumbling = [ { transform: 'rotate(0) translate3D(-50%, -50%, 0)', color: '#000' }, { color: '#431236', offset: 0.3}, { transform: 'rotate(360deg) translate3D(-50%, -50%, 0)', color: '#000' } ],
使用
offset
指定关键帧出现在动画位置的百分比, 首尾对象默认offset = 0/1
若不指定
offset
则直接选取中点作为offset
, 例如[ {/*...*/}, // <- 第一个元素, offset = 0 {offset: 0.4}, // <- 手动指定, offset = 0.4 {/*...*/}, // <- 未指定, 均分为 0.4 + (1-0.4)/3*1 = 0.6 {/*...*/}, // <- 未指定, 均分为 0.4 + (1-0.4)/3*2 = 0.8 {/*...*/} // <- 最后一个元素, offset = 1 ]
定义动画执行模式
const aliceTiming = { duration: 3000, iterations: Infinity }
执行动画
const anim = elem.animate(aliceTumbling, aliceTiming)
Hooks
- 下面的
anim
就是elem.animate()
的返回值 anim.play() / anim.pause()
: 执行 / 暂停动画. 注意, 动画在animate()
的时候回自动执行, 如需手动控制需要立刻pause
一下anim.playbackRate
: 动画执行速率(可以为负数), 可写属性anim.currentTime
: 动画执行时间, 可写属性anim.effect.timing.duration
: 动画持续时间, 类似属性见 MDNanim.finish()
: 动画结束anim.cancel()
: 终止动画Animation.reverse()
: 反向播放动画document.getAnimations()
: 获取全部 Web Animation 注册动画anim.onfinish(callback)
: 动画结束回调anim.oncancel(callback)
: 动画取消回调
- 下面的
FLIP 技术
当我们需要对 DOM 的位置做调整但是又不知道目标位置的具体时可以用 FLIP 实现带有过渡动画的位置调整.
例如, 有 a b c d
元素, 我们希望将元素变为 d c b a
. 可以直接通过 DOM API 调整位置, 但是无法实现动画. FLIP 的做法是先将元素的起始位置记下来, 再调整到目标位置, 再通过 CSS 将元素调到原为止, 最后通过动画完成过渡
- First:在任何事情发生之前,记录将要转换的元素的当前(即第一)位置和尺寸。您可以使用
element.getBoundingClientRect()
它,如下所示。 - Last:执行使过渡瞬间发生的代码,并记录元素的最终(即last)位置和尺寸。
- Invert:由于元素位于最后一个位置,我们想通过
transform
修改其位置和尺寸来创建它位于第一个位置的错觉。这需要一点数学运算,但并不难。 - Play:元素反转(并假装在第一个位置),我们可以通过将其设置为
transform
来将其移回到最后一个位置none
。
vue 2 文档中提到的效果
动画实现的比较
动画的实现方法:
- 纯 CSS (animation / transition) with GPU
- 纯 JS (requestAnimationFrame & style)
- WebAnimation API
其中
JS 实现的动画会被同步代码阻塞. 但是更加灵活
requestAnimationFrame(/*...*/) while(true); // <- request 卡死
不使用 GPU 渲染的 CSS 动画会被同步代码阻塞, 采用 GPU 的不阻塞. 只要动画涉及的属性不引起 reflow 动画的采样就会交给 GPU 处理.
#alice_css { animation: cssRound infinite 3s linear; } @keyframes cssRound { 0% { left: 0; /* <- left 会引发重绘, 此时动画的渲染是由 CPU 完成的, 如果执行 while(true); 动画就会卡死*/ } 100% { left: 300px; } }
#alice_css { animation: cssRound infinite 3s linear; } @keyframes cssRound { 0% { transform: rotateZ(0deg); /* <- rotateZ 不会引发重绘, 此时动画的渲染是由 GPU 完成的, 如果执行 while(true); 动画不会卡死*/ } 100% { transform: rotateZ(360deg); } }
因此, 能使用 CSS 动画的就不要用 JS 动画
WebAnimation API: 通过 JS 的方式定义动画, 最终会将动画效果通过 CSS 动画完成, 其对同步 JS 代码阻塞的表现与 CSS 动画一致
这个 API 既保留了 JS 的灵活性(控制动画执行, 动画执行观测, 与JS交互), 同时使用类似 CSS 的方式执行动画, 减少了同步代码对动画的影响
const aliceJs = document.getElementById('alice_js'); const aliceRoundJs = [ { transform: 'rotateZ(0deg)' }, // <- 同步代码阻塞也卡死 { transform: 'rotateZ(360deg)' }, ]; const aliceTimeJs = { duration: 3000, iterations: Infinity, }; aliceJs.animate(aliceRoundJs, aliceTimeJs);
const aliceJs = document.getElementById('alice_js'); const aliceRoundJs = [ { left: '0' }, // <- 同步代码阻塞也不卡死 { left: '300px' }, ]; const aliceTimeJs = { duration: 3000, iterations: Infinity, }; aliceJs.animate(aliceRoundJs, aliceTimeJs);