JavaScript 动画应该通过 requestAnimationFrame 实现。该内置方法允许设置回调函数,以便在浏览器准备重绘时运行。那通常很快,但确切的时间取决于浏览器。
callback 得到一个参数 —— 从页面加载开始经过的毫秒数。这个时间也可通过调用 performance.now() 得到。
当页面在后台时,根本没有重绘,因此回调将不会运行:动画将被暂停并且不会消耗资源。那很棒。
这是设置大多数动画的 helper 函数 animate:
function animate({ timing, draw, duration }) {
let start = performance.now();
requestAnimationFrame(function animate(time) {
// timeFraction 从 0 增加到 1
let timeFraction = (time - start) / duration;
if (timeFraction > 1) timeFraction = 1;
// 计算当前动画状态
let progress = timing(timeFraction);
draw(progress); // 绘制
if (timeFraction < 1) {
requestAnimationFrame(animate);
}
});
}
参数:
- duration —— 动画运行的总毫秒数。
- timing —— 计算动画进度的函数。获取从 0 到 1 的小数时间,返回动画进度,通常也是从 0 到 1。
- draw —— 绘制动画的函数。
当然我们可以改进它,增加更多花里胡哨的东西,但 JavaScript 动画不是经常用到。它们用于做一些有趣和不标准的事情。因此,您大可在必要时再添加所需的功能。
JavaScript 动画可以使用任何时序函数。与 CSS 不同,我们不仅限于 Bezier 曲线。
draw 也是如此:我们可以将任何东西动画化,而不仅仅是 CSS 属性。
练习:跳动的小球
http://js.jirengu.com/gucogeceve/1/edit?html,js,output
<!DOCTYPE HTML>
<html>
<head>
<script src="https://js.cx/libs/animate.js"></script>
<style>
#field {
height: 200px;
border-bottom: 3px black groove;
position: relative;
}
#ball {
position: absolute;
cursor: pointer;
}
</style>
</head>
<body>
<div id="field">
<img src="https://js.cx/clipart/ball.svg" width="40" height="40" id="ball">
</div>
<script>
function makeEaseOut(timing) {
return function(timeFraction) {
return 1 - timing(1 - timeFraction);
}
}
function bounce(timeFraction) {
for (let a = 0, b = 1, result; 1; a += b, b /= 2) {
if (timeFraction >= (7 - 4 * a) / 11) {
return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)
}
}
}
function quad(timeFraction) {
return Math.pow(timeFraction, 2);
}
ball.onclick = function() {
let height = field.clientHeight - ball.clientHeight;
let width = 100
animate({
duration: 2000,
timing: makeEaseOut(bounce),
draw(progress) {
ball.style.top = height * progress + 'px'
}
});
animate({
duration: 2000,
timing: makeEaseOut(quad),
draw(progress) {
ball.style.left = width * progress + 'px'
}
});
};
</script>
</body>
</html>