Javascript动画的实现原理浅析


Posted in Javascript onMarch 02, 2015

假设有这样一个动画功能需求:把一个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刷新一次),把间隔跟刷新率同步效果更好。

Javascript 相关文章推荐
Js Jquery创建一个弹出层可加载一个页面
May 08 Javascript
JavaScript数组对象实现增加一个返回随机元素的方法
Jul 27 Javascript
js模仿php中strtotime()与date()函数实现方法
Aug 11 Javascript
深入浅析JavaScript面向对象和原型函数
Feb 06 Javascript
50 个 jQuery 插件可将你的网站带到另外一个高度
Apr 26 Javascript
js实现页面跳转的几种方法小结
May 16 Javascript
Node.js + Redis Sorted Set实现任务队列
Sep 19 Javascript
D3.js实现直方图的方法详解
Sep 25 Javascript
js实现华丽的九九乘法表效果
Mar 29 Javascript
vue.js 使用axios实现下载功能的示例
Mar 05 Javascript
JS实现生成由字母与数字组合的随机字符串功能详解
May 25 Javascript
微信小程序上线发布流程图文详解
May 06 Javascript
JavaScript页面模板库handlebars的简单用法
Mar 02 #Javascript
EasyUI中实现form表单提交的示例分享
Mar 01 #Javascript
EasyUI实现二级页面的内容勾选的方法
Mar 01 #Javascript
EasyUI实现第二层弹出框的方法
Mar 01 #Javascript
EasyUI,点击开启编辑框,并且编辑框获得焦点的方法
Mar 01 #Javascript
浅谈EasyUI中Treegrid节点的删除
Mar 01 #Javascript
浅谈EasyUI中编辑treegrid的方法
Mar 01 #Javascript
You might like
使用JSON实现数据的跨域传输的php代码
2011/12/20 PHP
Codeigniter中禁止A Database Error Occurred错误提示的方法
2014/06/12 PHP
php简单防盗链实现方法
2015/07/29 PHP
php中简单的对称加密算法实现
2017/01/05 PHP
Laravel Validator 实现两个或多个字段联合索引唯一
2019/05/08 PHP
使用jscript实现二进制读写脚本代码
2008/06/09 Javascript
javascript 面向对象编程 万物皆对象
2009/09/17 Javascript
jQuery Ajax之load()方法
2009/10/12 Javascript
JavaScript 模拟用户单击事件
2009/12/31 Javascript
javascript 类型判断代码分析
2010/03/28 Javascript
计算世界完全对称日的js代码,粗糙版
2011/11/04 Javascript
JS记录用户登录次数实现代码
2014/01/15 Javascript
轻松理解JavaScript闭包
2017/03/14 Javascript
浅谈KOA2 Restful方式路由初探
2019/03/14 Javascript
Vue使用Canvas绘制图片、矩形、线条、文字,下载图片
2019/04/26 Javascript
Vue.js组件使用props传递数据的方法
2019/10/19 Javascript
Python 中开发pattern的string模板(template) 实例详解
2017/04/01 Python
Python基于更相减损术实现求解最大公约数的方法
2018/04/04 Python
python读取xlsx的方法
2018/12/25 Python
pip安装python库的方法总结
2019/08/02 Python
用Python徒手撸一个股票回测框架搭建【推荐】
2019/08/05 Python
详解Python图像处理库Pillow常用使用方法
2019/09/02 Python
python利用openpyxl拆分多个工作表的工作簿的方法
2019/09/27 Python
css和css3弹性盒模型实现元素宽度(高度)自适应
2019/05/15 HTML / CSS
CSS3动画和HTML5新特性详解
2020/08/31 HTML / CSS
COACH德国官方网站:纽约现代奢侈品牌,1941年
2018/06/09 全球购物
美国在线鞋类零售商:LifeStride
2019/06/09 全球购物
英语专业学生个人求职信范文
2014/01/06 职场文书
党建示范点实施方案
2014/03/12 职场文书
学生会竞聘书范文
2014/03/31 职场文书
学校领导班子对照检查材料
2014/08/28 职场文书
工厂员工辞职信范文
2015/05/12 职场文书
工资证明范本
2015/06/12 职场文书
2015年信息技术教研组工作总结
2015/07/22 职场文书
文书工作总结(范文)
2019/07/11 职场文书
Python使用Beautiful Soup(BS4)库解析HTML和XML
2022/06/05 Python