导语: 适当的使用动画可以是用户体验得到较高的提升
首先得理解下,什么是动画?
下面是摘自维基百科对于动画的定义:
动画是指由许多帧静止的画面,以一定的速度(如每秒16张)连续播放时,肉眼因视觉残象产生错觉,而误以为画面活动的作品。
非常易于理解的定义,我们看的视频、电影等影视作品,都是某种动画,一般像电影是1秒24张画面。
什么是JS动画
从某种角度,浏览器就是一个动画播放器、一个画布,通常以每秒60帧,也就是每秒60张画面,当然这个和视频、电影还是不同,主要在于浏览器是动态的计算每一帧的画面,然后展示的。
所谓的JS动画,就是通过JS代码来动态更改每一帧浏览器中元素的位置、颜色等属性,然后浏览器完成渲染绘制的工作,从而达到像在播放动画的效果。
JS动画比起CSS动画的最大好处就是可以有更好的控制粒度。
如何创作JS动画
基本循环
在浏览器中使用 requestAnimationFrame 方法可以创建一个动画循环
摘自MDN: window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
通过使用在raf中再次调用raf方法达到动画循环的效果。下面代码片段显示了,最简单的动画循环骨架。
function step(timestamp) {
/**
* 这里可以添加一些改变元素的位置、颜色等属性,从而触发浏览器重新渲染的逻辑
*/
// 继续循环,下一帧
window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step)
这是一个无线循环,不可能任何动画都是不断播放的没有止境的,一般动画都是有开始和结束时间点,也就是动画有一个持续的时间。这就需要我们能够比较好的控制每一帧的动画,从而达到各种效果。
动画控制
既然浏览器负责绘制渲染,所以JS的本质就是控制属性值根据某种规律的每一帧的变化。
比如把一个元素2s内线性右移 200px的距离,那么可以很容易计算出当前右移的距离. (200 / 2000 ) * 已持续的时间。下面是简单的代码事例:
let start = null
function step(timestamp) {
if (!start) start = timestamp
const element = document.getElementId('some-element');
// 动画已经执行的时间
const progress = timestamp - start;
// 播放到当前时间的值。 这里是一个线性的过程
const progressValue = (200 / 2000) * progress
// 向右移动到当前播放的位置
element.style.transform = `translateX(${Math.min(progressValue, 200)}px)`;
// 继续循环,下一帧。超过2s结束动画播放
if (progress < 2000) window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step)
缓动效果
缓动效果可以通过一个缓动函数(easing function)来实现。
所谓的缓动函数就是在一定的时间内控制参数的变化率 (Easing functions specify the rate of change of a parameter over time.)
让我们添加一个代码实例,实现一个简单的缓动效果
// 播放到当前时间的值。 这里是一个线性的过程
const progressValue = (200 / 2000) * progress
// 替换为下面。先慢后快的缓动效果
const elapsed = progress / duration
const easingCirc = 1 - Math.sqrt(1 - elapsed * elapsed)
const progressValue = (200 / 2000) * progress * easingCirc
通过缓动方程,可以改变动画的变更速率,从而实现各种缓动效果,在视觉体验上使得动画更加多变和有质感。
下面列几个简单的缓动函数
正弦:
t => 1 - Math.cos((t * Math.PI) / 2)
圆形:
t => 1 - Math.sqrt(1 - t * t)
后退:
t => t * t * (3 * t - 2)
具体可以查看下面网址,查看各种基础的缓动效果:https://easings.net/en
缓动效果也不仅限于上面网站中列举的,任何人都可以定义各种简单复杂的缓动函数,只要知道了这个套路,那么只要满足视觉需求,那么缓动函数可以是各种各样的🤔。还有一些较复杂的函数实现,比如:
- 弹簧(spring): https://webkit.org/demos/spring/spring.js (这是webkit的弹框效果的例子)
- 贝塞尔(bezier):https://github.com/gre/bezier-easing (贝塞尔缓动,这个可以模拟几乎任何的基础缓动效果,上面的一些例子本质就是一些曲线运动,而贝塞尔曲线可以模拟几乎任何的曲线,感兴趣的可以深入了解下)
动画流畅度思考
浏览器的刷新频率一般是 60 帧,当然可以保证60帧的播放动画是最好的,但是有时候因为性能的关系,无法达到,那么就会出现跳帧的现象,观感非常的不好。
动画的稳定,也就是保持一定的帧数,可能比一会儿40帧,一会儿60帧带来的效果更好。
现代的js动画库都会做大量的优化工作,为了性能的最大化,比如
- 缓动效果提前计算
- GPU加速
- CPU调度,延迟平缓
小小的加速技巧
有一个css属相叫做will-change,元素添加这个属性的并且指定某一些触发属性(scroll-position, transform, opacity 等),该属性会被提到单独的层通过GPU来预先渲染好,这样可以提高流畅度。当然使用太多也是有副作用的,具体可以看:https://developer.mozilla.org/en-US/docs/Web/CSS/will-change
为什么需要JS动画
需求简单的动画,可以非常方便的使用css来实现。
所以使用JS来制作控制动画的最大的好处就是可以精准的控制动画的过程,可以播放、暂停、重新开始等等,可以实现比较复杂的动画。但是会带来编码的复杂度,当然大多数时候我们不需要手动来造新的轮子,已经有比较好、数量也很多的js动画库存在。下面只是象征性的列举一些:
- anime.js: 轻量级但是强大的动画库
- gsap: 商用的,被誉为是性能最好,最强大的JS动画库
- velocityjs: 知名的,简单易用的js动画库