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 研究心得 取得属性的值
Nov 30 Javascript
jQuery 数据缓存data(name, value)详解及实现
Jan 04 Javascript
菜鸟javascript基础资料整理2
Dec 06 Javascript
JavaScript中的连字符详解
Nov 28 Javascript
javascript常用经典算法实例详解
Nov 25 Javascript
JS正则表达式修饰符中multiline(/m)用法分析
Dec 27 Javascript
Vue框架之goods组件开发详解
Jan 25 Javascript
JS实现的JSON数组去重算法示例
Apr 11 Javascript
js中的reduce()函数讲解
Jan 18 Javascript
关于layui的下拉搜索框异步加载数据的解决方法
Sep 28 Javascript
Layui带搜索的下拉框的使用以及动态数据绑定方法
Sep 28 Javascript
webgl实现物体描边效果的方法介绍
Nov 27 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
php Static关键字实用方法
2010/06/04 PHP
关于PHP语言构造器介绍
2013/07/08 PHP
PHP函数http_build_query使用详解
2014/08/20 PHP
PHP设计模式之观察者模式实例
2016/02/22 PHP
javascript onkeydown,onkeyup,onkeypress,onclick,ondblclick
2009/02/04 Javascript
基于jQuery的Spin Button自定义文本框数值自增或自减
2010/07/17 Javascript
jQuery选择器简明总结(含用法实例,一目了然)
2014/04/25 Javascript
jquery ajax请求方式与提示用户正在处理请稍等
2014/09/01 Javascript
jQuery 插件开发指南
2014/11/14 Javascript
详解JavaScript语言的基本语法要求
2015/11/20 Javascript
jquery 属性选择器(匹配具有指定属性的元素)
2016/09/06 Javascript
JavaScript中String对象的方法介绍
2017/01/04 Javascript
jQuery插件FusionCharts绘制的3D双柱状图效果示例【附demo源码】
2017/04/20 jQuery
小程序分页实践之编写可复用分页组件
2019/07/18 Javascript
Vue实现滑动拼图验证码功能
2019/09/15 Javascript
[04:27]DOTA2官方论坛水友赛集锦
2013/09/16 DOTA
[46:02]DOTA2上海特级锦标赛D组资格赛#2 Liquid VS VP第二局
2016/02/28 DOTA
从零学python系列之从文件读取和保存数据
2014/05/23 Python
解决Python3中的中文字符编码的问题
2018/07/18 Python
Python双向循环链表实现方法分析
2018/07/30 Python
python 从文件夹抽取图片另存的方法
2018/12/04 Python
python requests post多层字典的方法
2018/12/27 Python
详解Python logging调用Logger.info方法的处理过程
2019/02/12 Python
Python常用爬虫代码总结方便查询
2019/02/25 Python
python使用flask与js进行前后台交互的例子
2019/07/19 Python
你可能不知道的Python 技巧小结
2020/01/29 Python
css3强大的动画效果animate使用说明及浏览器兼容介绍
2013/01/09 HTML / CSS
HTML5单页面手势滑屏切换原理分析
2017/07/10 HTML / CSS
记一次高分屏下canvas模糊问题
2020/02/17 HTML / CSS
世界上最大的艺术社区:SAA
2020/12/30 全球购物
咨询公司各岗位职责
2013/12/02 职场文书
马云北大演讲完整版:真心话,什么才是阿里的核心竞争力?
2014/04/04 职场文书
“九一八事变纪念日”国旗下讲话稿
2014/09/14 职场文书
求职信范文怎么写
2015/03/19 职场文书
为什么MySQL分页用limit会越来越慢
2021/07/25 MySQL
CSS实现两列布局的N种方法
2021/08/02 HTML / CSS