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 相关文章推荐
结合JQ1.9通过js正则判断各种浏览器版本的方法
Dec 30 Javascript
AngularJS基础 ng-model-options 指令简单示例
Aug 02 Javascript
vue 2.0路由之路由嵌套示例详解
May 08 Javascript
ionic App问题总结系列之ionic点击系统返回键退出App
Aug 19 Javascript
浅谈Angular4实现热加载开发旅程
Sep 08 Javascript
vue动态删除从数据库倒入列表的某一条方法
Sep 29 Javascript
使用JQuery自动完成插件Auto Complete详解
Jun 18 jQuery
利用不到200行代码写一款属于你自己的js类库
Jul 08 Javascript
vue3.0中的双向数据绑定方法及优缺点
Aug 01 Javascript
element ui分页多选,翻页记忆的实例
Sep 03 Javascript
JavaScript中变量提升机制示例详解
Dec 27 Javascript
token 机制和实现方式
Dec 15 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中使用X-SendFile头让文件下载更快
2014/06/01 PHP
PHP遍历XML文档所有节点的方法
2015/03/12 PHP
在laravel中使用with实现动态添加where条件
2019/10/10 PHP
Laravel 5.4前后台分离,通过不同的二级域名访问方法
2019/10/13 PHP
laravel框架语言包拓展实现方法分析
2019/11/22 PHP
js版本A*寻路算法
2006/12/22 Javascript
使用javascript:将其它类型值转换成布尔类型值的解决方法详解
2013/05/07 Javascript
js控制表单不能输入空格的小例子
2013/11/20 Javascript
jQuery.parseJSON(json)将JSON字符串转换成js对象
2014/07/27 Javascript
Jquery技巧(必须掌握)
2016/03/16 Javascript
基于Echarts 3.19 制作常用的图形(非静态)
2016/05/19 Javascript
AngularJS 与Bootstrap实现表格分页实例代码
2016/10/14 Javascript
Bootstrap免费字体和图标网站(值得收藏)
2017/03/16 Javascript
vue实现表格增删改查效果的实例代码
2017/07/18 Javascript
封装运动框架实战左右与上下滑动的焦点轮播图(实例)
2017/10/17 Javascript
javaScript字符串工具类StringUtils详解
2017/12/08 Javascript
Vue服务器渲染Nuxt学习笔记
2018/01/31 Javascript
学习jQuery中的noConflict()用法
2018/09/28 jQuery
详解vue中多个有顺序要求的异步操作处理
2019/10/29 Javascript
Vue中rem与postcss-pxtorem的应用详解
2019/11/20 Javascript
用云开发Cloudbase实现小程序多图片内容安全监测的代码详解
2020/06/07 Javascript
Python实现统计英文单词个数及字符串分割代码
2015/05/28 Python
Python错误的处理方法
2020/06/23 Python
Ashford台湾:以折扣价提供奢华的男女用表款
2019/12/04 全球购物
庆祝教师节活动方案
2014/01/31 职场文书
英语专业个人求职信范文
2014/02/01 职场文书
创业者迈进成功第一步:如何写创业计划书?
2014/03/22 职场文书
幼儿园评语大全
2014/04/17 职场文书
慰问敬老院活动总结
2014/04/26 职场文书
应届生求职信范文
2014/05/26 职场文书
企业形象策划方案
2014/05/29 职场文书
解除劳动关系协议书2篇
2014/11/28 职场文书
教师工作态度自我评价
2015/03/05 职场文书
2016年秋季运动会广播稿
2015/12/21 职场文书
自荐信范文
2019/05/20 职场文书
详解如何用Python实现感知器算法
2021/06/18 Python