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 相关文章推荐
JQuery 表单中textarea字数限制实现代码
Dec 07 Javascript
原生js配合cookie制作保存路径的拖拽
Dec 29 Javascript
JavaScript过滤字符串中的中文与空格方法汇总
Mar 07 Javascript
JS延时器提示框的应用实例代码解析
Apr 27 Javascript
浅谈js中的延迟执行和定时执行
May 31 Javascript
js实现添加可信站点、修改activex安全设置,禁用弹出窗口阻止程序
Aug 17 Javascript
Angularjs CURD 详解及实例代码
Sep 14 Javascript
微信小程序技巧之show内容展示,上传文件编码问题
Jan 23 Javascript
Web纯前端“旭日图”实现元素周期表
Mar 10 Javascript
angular之ng-template模板加载
Nov 09 Javascript
vue 的点击事件获取当前点击的元素方法
Sep 15 Javascript
详解滑动穿透(锁body)终极探索
Apr 16 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
介绍几个array库的新函数 php
2006/12/29 PHP
php实现过滤表单提交中html标签的方法
2014/10/17 PHP
基于JQuery+PHP编写砸金蛋中奖程序
2015/09/08 PHP
thinkphp3.2点击刷新生成验证码
2016/02/16 PHP
php实现的中秋博饼游戏之绘制骰子图案功能示例
2017/11/06 PHP
浅谈php使用curl模拟多线程发送请求
2019/03/08 PHP
提高Laravel应用性能方法详解
2019/06/24 PHP
PHP实现的AES 128位加密算法示例
2019/09/16 PHP
Yii2.0框架behaviors方法使用实例分析
2019/09/30 PHP
jQuery Pagination Ajax分页插件(分页切换时无刷新与延迟)中文翻译版
2013/01/11 Javascript
Jquery的hide及toggle方法让超链接慢慢消失
2013/09/06 Javascript
10分钟掌握XML、JSON及其解析
2020/12/06 Javascript
详解Javascript百度地图接口开发文档中的类和方法
2017/02/07 Javascript
React Native 自定义下拉刷新上拉加载的列表的示例
2018/03/01 Javascript
解决vue props 拿不到值的问题
2018/09/11 Javascript
bootstrap下拉分页样式 带跳转页码
2018/12/29 Javascript
用Vue编写抽象组件的方法
2019/05/06 Javascript
vue中的过滤器及其时间格式化问题
2020/04/09 Javascript
jQuery实现简单日历效果
2020/07/05 jQuery
[00:38]TI珍贵瞬间系列(二):笑
2020/08/26 DOTA
使用python实现递归版汉诺塔示例(汉诺塔递归算法)
2014/04/08 Python
python 采集中文乱码问题的完美解决方法
2016/09/27 Python
解决Python内层for循环如何break出外层的循环的问题
2019/06/24 Python
python3 实现爬取TOP500的音乐信息并存储到mongoDB数据库中
2019/08/24 Python
PyCharm使用之配置SSH Interpreter的方法步骤
2019/12/26 Python
使用pyplot.matshow()函数添加绘图标题
2020/06/16 Python
python开发一个解析protobuf文件的简单编译器
2020/11/17 Python
html5 svg 中元素点击事件添加方法
2013/01/16 HTML / CSS
HTML5去掉输入框type为number时的上下箭头的实现方法
2020/01/03 HTML / CSS
如何保障Web服务器安全
2014/05/05 面试题
设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。
2014/12/30 面试题
个人安全生产承诺书
2014/05/22 职场文书
倡导文明标语
2014/06/16 职场文书
小学感恩节活动总结
2015/03/24 职场文书
家长对孩子的寒假评语
2015/10/09 职场文书
2016年师德先进个人事迹材料
2016/02/29 职场文书