Javascript动画的实现原理浅析
作者:bea
假设有这样一个动画功能需求:把一个div的宽度从100px变化到200px。写出来的代码可能是这样的: 代码如下: <div id="test1" style="width: 100px; height: 100px; background: blue; color: white;"></div> function animate1(element, endValue, duration) { var startTime = new Dat
假设有这样一个动画功能需求:把一个div的宽度从100px变化到200px。写出来的代码可能是这样的:
代码如下:
<div id="test1" style="width: 100px; height: 100px; background: blue; color: white;"></div>
function animate1(element, endValue, duration) {
var startTime = new Date(),
startValue = parseInt(element.style.width),
step = 1;
var timerId = setInterval(function() {
var nextValue = parseInt(element.style.width) + step;
element.style.width = nextValue + 'px';
if (nextValue >= endValue) {
clearInterval(timerId);
// 显示动画耗时
element.innerHTML = new Date - startTime;
}
}, duration / (endValue - startValue) * step);
}
animate1(document.getElementById('test1'), 200, 1000);
原理是每隔一定时间增加1px,一直到200px为止。然而,动画结束后显示的耗时却不止1s(一般是1.5s左右)。究其原因,是因为setInterval并不能严格保证执行间隔。
有没有更好的做法呢?下面先来看一道小学数学题:
代码如下:
A楼和B楼相距100米,一个人匀速从A楼走到B楼,走了5分钟到达目的地,问第3分钟时他距离A楼多远?
匀速运动中计算某个时刻路程的计算公式为:路程 * 当前时间 / 时间 。所以答案应为 100 * 3 / 5 = 60 。
这道题带来的启发是,某个时刻的路程是可以通过特定公式计算出来的。同理,动画过程中某个时刻的值也可以通过公式计算出来,而不是累加得出:
代码如下:
<div id="test2" style="width: 100px; height: 100px; background: red; color: white;"></div>
function animate2(element, endValue, duration) {
var startTime = new Date(),
startValue = parseInt(element.style.width);
var timerId = setInterval(function() { var percentage = (new Date - startTime) / duration;
var stepValue = startValue + (endValue - startValue) * percentage; element.style.width = stepValue + 'px';
if (percentage >= 1) { clearInterval(timerId); element.innerHTML = new Date - startTime; } }, 13); }
animate2(document.getElementById('test2'), 200, 1000);
这样改良之后,可以看到动画执行耗时最多只会有10几ms的误差。但是问题还没完全解决,在浏览器开发工具中检查test2元素可以发现,test2的最终宽度可能不止200px。仔细检查animate2函数的代码可以发现:
1.percentage的值可能大于1,可以通过Math.min限制最大值解决。 2.即使保证了percentage的值不大于1,只要endValue或startValue为小数,(endValue - startValue) * percentage的值也可能产生误差,因为Javascript小数运算的精度不够。其实我们要保证的只是最终值的准确性,所以在percentage为1的时候,直接使用endValue即可。
于是,animate2函数的代码修改为:
代码如下:
function animate2(element, endValue, duration) {
var startTime = new Date(),
startValue = parseInt(element.style.width);
var timerId = setInterval(function() { // 保证百分率不大于1 var percentage = Math.min(1, (new Date - startTime) / duration);
var stepValue; if (percentage >= 1) { // 保证最终值的准确性 stepValue = endValue; } else { stepValue = startValue + (endValue - startValue) * percentage; } element.style.width = stepValue + 'px';
if (percentage >= 1) { clearInterval(timerId); element.innerHTML = new Date - startTime; } }, 13); }
还有最后一个疑问:setInterval的间隔为何设为13ms?原因是当下显示器的刷新率一般不超过75Hz(即每秒刷新75次,也就是每隔约13ms刷新一次),把间隔跟刷新率同步效果更好。
有用 | 无用
代码如下:
<div id="test1" style="width: 100px; height: 100px; background: blue; color: white;"></div>
function animate1(element, endValue, duration) {
var startTime = new Date(),
startValue = parseInt(element.style.width),
step = 1;
var timerId = setInterval(function() {
var nextValue = parseInt(element.style.width) + step;
element.style.width = nextValue + 'px';
if (nextValue >= endValue) {
clearInterval(timerId);
// 显示动画耗时
element.innerHTML = new Date - startTime;
}
}, duration / (endValue - startValue) * step);
}
animate1(document.getElementById('test1'), 200, 1000);
原理是每隔一定时间增加1px,一直到200px为止。然而,动画结束后显示的耗时却不止1s(一般是1.5s左右)。究其原因,是因为setInterval并不能严格保证执行间隔。
有没有更好的做法呢?下面先来看一道小学数学题:
代码如下:
A楼和B楼相距100米,一个人匀速从A楼走到B楼,走了5分钟到达目的地,问第3分钟时他距离A楼多远?
匀速运动中计算某个时刻路程的计算公式为:路程 * 当前时间 / 时间 。所以答案应为 100 * 3 / 5 = 60 。
这道题带来的启发是,某个时刻的路程是可以通过特定公式计算出来的。同理,动画过程中某个时刻的值也可以通过公式计算出来,而不是累加得出:
代码如下:
<div id="test2" style="width: 100px; height: 100px; background: red; color: white;"></div>
function animate2(element, endValue, duration) {
var startTime = new Date(),
startValue = parseInt(element.style.width);
var timerId = setInterval(function() { var percentage = (new Date - startTime) / duration;
var stepValue = startValue + (endValue - startValue) * percentage; element.style.width = stepValue + 'px';
if (percentage >= 1) { clearInterval(timerId); element.innerHTML = new Date - startTime; } }, 13); }
animate2(document.getElementById('test2'), 200, 1000);
这样改良之后,可以看到动画执行耗时最多只会有10几ms的误差。但是问题还没完全解决,在浏览器开发工具中检查test2元素可以发现,test2的最终宽度可能不止200px。仔细检查animate2函数的代码可以发现:
1.percentage的值可能大于1,可以通过Math.min限制最大值解决。 2.即使保证了percentage的值不大于1,只要endValue或startValue为小数,(endValue - startValue) * percentage的值也可能产生误差,因为Javascript小数运算的精度不够。其实我们要保证的只是最终值的准确性,所以在percentage为1的时候,直接使用endValue即可。
于是,animate2函数的代码修改为:
代码如下:
function animate2(element, endValue, duration) {
var startTime = new Date(),
startValue = parseInt(element.style.width);
var timerId = setInterval(function() { // 保证百分率不大于1 var percentage = Math.min(1, (new Date - startTime) / duration);
var stepValue; if (percentage >= 1) { // 保证最终值的准确性 stepValue = endValue; } else { stepValue = startValue + (endValue - startValue) * percentage; } element.style.width = stepValue + 'px';
if (percentage >= 1) { clearInterval(timerId); element.innerHTML = new Date - startTime; } }, 13); }
还有最后一个疑问:setInterval的间隔为何设为13ms?原因是当下显示器的刷新率一般不超过75Hz(即每秒刷新75次,也就是每隔约13ms刷新一次),把间隔跟刷新率同步效果更好。
有用 | 无用
猜你喜欢
您可能感兴趣的文章:
- JS实现点击文字对应DIV层不停闪动效果的方法
- JavaScript定时显示广告代码分享
- js实现绿白相间竖向网页百叶窗动画切换效果
- js实现横向百叶窗效果网页切换动画效果的方法
- js实现从中间开始往上下展开网页窗口的方法
- Javascript无参数和有参数类继承问题解决方法
- JS实现超炫网页烟花动画效果的方法
- ECMAScript 5严格模式(Strict Mode)介绍
- Javascript中使用A标签获取当前目录的绝对路径方法
- JavaScript日期类型的一些用法介绍
- 了解Javascript的模块化开发
- JS实现的4种数字千位符格式化方法分享
- js实现图片漂浮效果的方法
- ECMAScript 5中的属性描述符详解
- JS+CSS实现可以凹陷显示选中单元格的方法
- JavaScript数组常用方法
- 使用npm发布Node.JS程序包教程
- js实现点击链接后窗口缩小并居中的方法
- 运行Node.js的IIS扩展iisnode安装配置笔记