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 相关文章推荐
Prototype Number对象 学习
Jul 19 Javascript
妙用Jquery的val()方法
Jun 27 Javascript
HTML Color Picker(js拾色器效果)
Aug 27 Javascript
jQuery回调函数的定义及用法实例
Dec 23 Javascript
jquery实现的Banner广告收缩效果代码
Sep 02 Javascript
js动态添加的DIV中的onclick事件简单实例
Jul 25 Javascript
响应式表格之固定表头的简单实现
Aug 26 Javascript
angular-ngSanitize模块-$sanitize服务详解
Jun 13 Javascript
Vue计算属性的使用
Aug 04 Javascript
antd Upload 文件上传的示例代码
Dec 14 Javascript
Vue使用.sync 实现父子组件的双向绑定数据问题
Apr 04 Javascript
javascript实现导航栏分页效果
Jun 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
Views rows style模板重写代码
2011/05/16 PHP
使用phpQuery采集网页的方法
2013/11/13 PHP
php延迟静态绑定实例分析
2015/02/08 PHP
优化WordPress中文章与评论的时间显示
2016/01/12 PHP
PHP简单获取随机数的常用方法小结
2017/06/07 PHP
Laravel学习教程之model validation的使用示例
2017/10/23 PHP
多浏览器支持的右下角浮动窗口
2010/04/01 Javascript
jquery 学习之二 属性 文本与值(text,val)
2010/11/25 Javascript
解决jquery1.9不支持browser对象的问题
2013/11/13 Javascript
jquery 自定义容器下雨效果可将下雨图标改为其他
2014/04/23 Javascript
jquery 选取方法都有哪些
2014/05/18 Javascript
js限制文本框只能输入中文的方法
2015/08/11 Javascript
js实现Select头像选择实时预览代码
2015/08/17 Javascript
JS实现具备延时功能的滑动门菜单效果
2015/09/17 Javascript
创建自己的jquery表格插件
2015/11/25 Javascript
深入浅析Bootstrap列表组组件
2016/05/03 Javascript
javascript cookie基础应用之记录用户名的方法
2016/09/20 Javascript
老生常谈的跨域处理
2017/01/11 Javascript
jquery.cookie.js的介绍与使用方法
2017/02/09 Javascript
浅谈React Native 中组件的生命周期
2017/09/08 Javascript
webpack 样式加载的实现原理
2018/06/12 Javascript
详解Element 指令clickoutside源码分析
2019/02/15 Javascript
JS实现指定区域的全屏显示功能示例
2019/04/25 Javascript
JavaScript类的继承多种实现方法
2020/05/30 Javascript
python+opencv轮廓检测代码解析
2018/01/05 Python
Python json模块dumps、loads操作示例
2018/09/06 Python
详解python中的index函数用法
2019/08/06 Python
Python迭代器协议及for循环工作机制详解
2020/07/14 Python
婚庆司仪主持词
2014/03/15 职场文书
公司新人试用期自我评价
2014/09/17 职场文书
群众路线班子对照检查材料
2014/09/25 职场文书
2016春季运动会前导词
2015/11/25 职场文书
Python+OpenCV实现在图像上绘制矩形
2022/03/21 Python
Redis分布式锁的7种实现
2022/04/01 Redis
基于Python实现股票收益率分析
2022/04/02 Python
Django框架中模型的用法
2022/06/10 Python