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判断IE版本的代码 超管用!
Aug 09 Javascript
JavaScript高级程序设计阅读笔记(六) ECMAScript中的运算符(二)
Feb 27 Javascript
JavaScript获取某年某月的最后一天附截图
Jun 23 Javascript
JS简单实现DIV相对于浏览器固定位置不变的方法
Jun 17 Javascript
一个炫酷的Bootstrap导航菜单
Dec 28 Javascript
three.js 入门案例详解
Jan 23 Javascript
详解VueJs中的V-bind指令
May 03 Javascript
浅谈layer的Icon样式以及一些常用的layer窗口使用方法
Sep 11 Javascript
使用p5.js实现动态GIF图片临摹重现
Oct 23 Javascript
node.js中对Event Loop事件循环的理解与应用实例分析
Feb 14 Javascript
详解Typescript 内置的模块导入兼容方式
May 31 Javascript
在项目vue中使用echarts的操作步骤
Sep 07 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
超外差式晶体管收音机的组装与统调
2021/03/01 无线电
php 常用类汇总 推荐收藏
2010/05/13 PHP
php 中文和编码判断代码
2010/05/16 PHP
php数组函数序列之next() - 移动数组内部指针到下一个元素的位置,并返回该元素值
2011/10/31 PHP
PHP连接SQLServer2005方法及代码
2013/12/26 PHP
PHP中使用FFMPEG获取视频缩略图和视频总时长实例
2014/05/04 PHP
一段实用的php验证码函数
2016/05/19 PHP
PHP获取指定日期是星期几的实现方法
2016/11/30 PHP
同一页面多个商品倒计时JS 基于面向对象的javascript
2012/02/16 Javascript
跨浏览器的事件对象介绍
2012/06/27 Javascript
JavaScript高级程序设计阅读笔记(十六) javascript检测浏览器和操作系统-detect.js
2012/08/14 Javascript
javascript的parseFloat()方法精度问题探讨
2013/11/26 Javascript
基于JS快速实现导航下拉菜单动画效果附源码下载
2016/10/27 Javascript
微信小程序 wx.uploadFile无法上传解决办法
2016/12/14 Javascript
cocos2dx+lua实现橡皮擦功能
2018/12/20 Javascript
React+TypeScript+webpack4多入口配置详解
2019/08/08 Javascript
layui 上传文件_批量导入数据UI的方法
2019/09/23 Javascript
Vue v-for循环之@click点击事件获取元素示例
2019/11/09 Javascript
element el-tree组件的动态加载、新增、更新节点的实现
2020/02/27 Javascript
Javascript模拟实现new原理解析
2020/03/03 Javascript
Element InputNumber计数器的使用方法
2020/07/27 Javascript
Python SMTP发送邮件遇到的一些问题及解决办法
2018/10/24 Python
python实现最小二乘法线性拟合
2019/07/19 Python
利用Python校准本地时间的方法教程
2019/10/31 Python
python实现飞行棋游戏
2020/02/05 Python
使用Python爬取弹出窗口信息的实例
2020/03/14 Python
解决PyCharm IDE环境下,执行unittest不生成测试报告的问题
2020/09/03 Python
python中K-means算法基础知识点
2021/01/25 Python
中国网上药店领导者:1药网
2017/02/16 全球购物
Nip + Fab官网:英国美容品牌
2019/08/26 全球购物
国贸专业个人求职信分享
2013/12/04 职场文书
中学自我评价
2014/01/31 职场文书
2014财务部年度工作总结
2014/12/08 职场文书
幼儿园奖惩制度范本
2015/08/05 职场文书
热爱劳动主题班会
2015/08/14 职场文书
nginx 反向代理之 proxy_pass的实现
2021/03/31 Servers