顺滑的动画
在网页开发圈子里有一种误解,那就是认为CSS动画是网络中唯一可以实现高性能动画的方法。因为CSS可以借助硬件加速把动画直接交给GPU处理。
事实上,基于JavaScript的动画与基于CSS的动画一样快。之所以JS动画会卡顿,或许是因为你使用定时器(setTimeout、setInterval)去处理动画了,但这不是JS的错,而是你的使用方法错了。
基于JS的动画有如下的优点:
1.动画中止
2.动画反转
3.动画管理
CSS非常适合实现悬停状态的动画效果(例如:当鼠标位于链接上方时,链接变成蓝色)这类的微互动,这也是通常情况下基本的网页所包含的动画。
Velocity.js核心原理
Velocity.js是一个著名的动画库。这是个轻量级的库,但是功能却异常丰富。
GitHub地址奉上:github.com/julianshapi…
核心JS是:
document.querySelector("#v").velocity(
{
opacity: 0.5,
left: 1000,
},
{
duration: 2000,
}
);
下图是帧率和CPU利用率的示意图:
[<img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f1bd3bce5b6b41d5bfbf27ee182ba47c~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image)](https://link.juejin.cn/?target=14.png “14.png”” style=“margin: auto” />
在CPU那一行,可以看到,有很多执行点,我们放大其中一个:
[<img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ffd390c1b06246be82d3496091cf571c~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image)](https://link.juejin.cn/?target=4.png “4.png”” style=“margin: auto” />
我们可以看到Animation Frame Fired
,其实就是requestAnimationFrame()
回调被触发了,接着执行tick和setPropertyValue。我们一个一个看:
[<img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a68c4c4960754413b9b42f2e4e923dee~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image)](https://link.juejin.cn/?target=7.png “7.png”” style=“margin: auto” />
点击跳转到源码:
[<img src=“https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a29f34382d3e44d796a37c92871c0bde~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image)](https://link.juejin.cn/?target=3.png “3.png”” style=“margin: auto” />
跳转到rAFShim,可以看到这是对 requestAnimationFrame
的调用,tick
是它的回调:
rAFProxy = (callback: FrameRequestCallback) => {
return setTimeout(callback, Math.max(0, FRAME_TIME - (performance.now() - lastTick)));
},
rAFShim = window.requestAnimationFrame || rAFProxy;
跳转到 setPropertyValue
:
function setPropertyValue(element, propertyName, propertyValue, fn) {var noCache = NoCacheNormalizations.has(propertyName),data = !noCache && Data(element);if (noCache || data && data.cache[propertyName] !== propertyValue) {// By setting it to undefined we force a true "get" laterif (!noCache) {data.cache[propertyName] = propertyValue || undefined;}fn = fn || getNormalization(element, propertyName);if (fn) {fn(element, propertyValue);}if (Velocity$$1.debug >= 2) {console.info("Set "" + propertyName + "": "" + propertyValue + """, element);}}
}
我们继续深究上面的fn函数,发现它是 getSetStyle
返回的函数,函数最后会设置元素的style属性:
function getSetStyle(propertyName) {return function (element, propertyValue) {if (propertyValue === undefined) {return computePropertyValue(element, propertyName);}element.style[propertyName] = propertyValue;};
}
至此,我们可以理顺 Velocity.js 的核心原理:
1.当我们通过velocity去设置某个元素的最终样式,velocity会计算出初始值,在演示里是 opacity 和 left
2.然后通过注册requestAnimationFrame()
回调去计算动画的中间态,并应用在元素上
Velocity.js 是通过计算值处理动画效果的中间值的。所以对几何长宽、颜色、透明度这些值非常友好。
不仅如此,Velocity.js还可以终止动画,也可以反转动画,这些都是CSS设置动画办不到的,JS可以通过在执行requestAnimationFrame
回调前终止动画,并且通过记录历史中间态并反转。
搭配React使用
Velocity.js 搭配 React 使用的库叫 velocity-react。
GitHub地址奉上:github.com/google-fabr…
velocity-react 暴露两个组件接口:
1.VelocityComponent,添加动画,为子组件注册动画效果;
2.VelocityTransitionGroup,添加过渡效果(显示和隐藏组件,切换组件),这个组件里面的所有子组件的mount、unmount都会触发过渡效果,底层依赖TransitionGroup
(react-transition-group)组件。
还有一个API接口:velocityHelpers,用以注册动画效果的。