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 相关文章推荐
aspx中利用js实现确认删除代码
Jul 22 Javascript
Javascript Request获取请求参数如何实现
Nov 28 Javascript
JavaScript中常见获取元素的方法汇总
Mar 04 Javascript
在Linux系统中搭建Node.js开发环境的简单步骤讲解
Jan 26 Javascript
浅谈JavaScript 中有关时间对象的方法
Aug 15 Javascript
JS 判断某变量是否为某数组中的一个值的3种方法(总结)
Jul 10 Javascript
如何编写一个完整的Angular4 FormText 组件
Nov 18 Javascript
使用node打造自己的命令行工具方法教程
Mar 26 Javascript
Vue filter介绍及详细使用
Apr 04 Javascript
微信小程序 Storage更新详解
Jul 16 Javascript
Vue 中使用lodash对事件进行防抖和节流操作
Jul 26 Javascript
nuxt 页面路由配置,主页轮播组件开发操作
Nov 05 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缓存函数的使用说明
2013/05/10 PHP
php jq jquery getJSON跨域提交数据完整版
2013/09/13 PHP
Thinkphp框架开发移动端接口(1)
2016/08/18 PHP
JavaScript 继承详解(二)
2009/07/13 Javascript
JavaScript与Div对层定位和移动获得坐标的实现代码
2010/09/08 Javascript
js实现倒计时(距离结束还有)示例代码
2013/07/24 Javascript
JavaScript移除数组元素减少长度的方法
2013/09/05 Javascript
jquery实现可拖动DIV自定义保存到数据的实例
2013/11/20 Javascript
js实现网页标题栏闪烁提示效果实例分析
2014/11/20 Javascript
使用jQuery和Bootstrap实现多层、自适应模态窗口
2014/12/22 Javascript
jquery仿百度经验滑动切换浏览效果
2015/04/14 Javascript
基于JS代码实现导航条弹出式悬浮菜单
2016/06/17 Javascript
浅谈JavaScript中的this指针和引用知识
2016/08/05 Javascript
jquery实时获取时间的简单实例
2017/01/26 Javascript
js仿淘宝评价评分功能
2017/02/28 Javascript
JavaScript类的继承方法小结【组合继承分析】
2018/07/11 Javascript
angular 数据绑定之[]和{{}}的区别
2018/09/25 Javascript
解决layui数据表格排序图标被超出的表头挤出去的问题
2019/09/19 Javascript
vue-property-decorator用法详解
2019/12/12 Javascript
Python实现的生成自我描述脚本分享(很有意思的程序)
2014/07/18 Python
讲解Python的Scrapy爬虫框架使用代理进行采集的方法
2016/02/18 Python
对python判断是否回文数的实例详解
2019/02/08 Python
Django项目主urls导入应用中views的红线问题解决
2019/08/10 Python
关于Tensorflow使用CPU报错的解决方式
2020/02/05 Python
python数据类型可变不可变知识点总结
2020/03/06 Python
python 删除excel表格重复行,数据预处理操作
2020/07/06 Python
Eagle Eyes Optics鹰眼光学:高性能太阳镜
2018/12/07 全球购物
匈牙利超级网上商店和优惠:Alza.hu
2019/12/17 全球购物
商场拾金不昧表扬信
2014/01/13 职场文书
九年级政治教学反思
2014/02/06 职场文书
幼儿园门卫岗位职责
2014/02/14 职场文书
酒店管理专业自荐信
2014/05/23 职场文书
2016年主题党日活动总结
2016/04/05 职场文书
如何写好闭幕词
2019/04/02 职场文书
2019年员工晋升管理制度范本!
2019/07/08 职场文书
python 爬取京东指定商品评论并进行情感分析
2021/05/27 Python