Javascript动画的实现原理浅析

假设有这样一个动画功能需求:把一个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刷新一次),把间隔跟刷新率同步效果更好。

(0)

相关推荐

  • javascript 流畅动画实现原理

    当然考虑到浏览器(特别指IE)糟糕的js执行能力,动画效果又要受到影响. 浏览器中的动画效果主要依靠js来动态改变Dom元素外观来形成.不过据说css正在修炼这方面的能力哦.(拭目以待^_^) 我们需要周期性的改变dom元素的外观,这个周期性就要依靠setTimeout()和setInterval()来完成. 它们2个哪一个更好点呢 见这里 (是jquery的作者写的一个文章) 我个人比较偏向setInterval,setTimeout需要用递归调用而且在线程很忙的情况下会延时,这会影响流畅性.

  • Javascript动画的实现原理浅析

    假设有这样一个动画功能需求:把一个div的宽度从100px变化到200px.写出来的代码可能是这样的: 复制代码 代码如下: <div id="test1" style="width: 100px; height: 100px; background: blue; color: white;"></div> function animate1(element, endValue, duration) {     var startTime =

  • Javascript自执行匿名函数(function() { })()的原理浅析

    函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数指没有指定函数名或指针的函数,自执行匿名函数只是其中一种,下文中称这种函数为:自执行函数 下面是一个最常见的自执行函数: // 传统匿名函数 (function() { alert('hello'); })(); 这段代码的执行效果就是在页面再载入时弹出:"hello" 是什么促使它自动执行的?,来看下面的代码 // 在传统写法上去掉小括号,并在前面加上运算符 ~,!,+,- ~function(){

  • 浅析JavaScript动画

    今天,小学生以自己浅薄的见地,在前辈大能的基础上写这篇文章,希望给大家打开一扇窥探JavaScript(以下简称JS)动画的窗户. JS如何制造出动画效果? 结合浏览器提供的 setInterval 或 setTimeout API,高频改变DOM元素的一些属性,即可创造一个肉眼可见的动画效果.一个看起来非常流畅的JS动画除了需要良好的变换算法外,与其执行宿主也是非不开的.程序写得再好,如果浏览器过于老旧,电脑CPU性能低下,也会出现卡顿,甚至卡死. 执行一个动画函数对于浏览器来说是个苦差,设置

  • JavaScript观察者模式(publish/subscribe)原理与实现方法

    本文实例讲述了JavaScript观察者模式(publish/subscribe)原理与实现方法.分享给大家供大家参考,具体如下: 观察者模式又叫做发布订阅模式,它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生改变时就会通知所有观察着对象.它是由两类对象组成,主题和观察者,主题负责发布事件,同时观察者通过订阅这些事件来观察该主体,发布者和订阅者是完全解耦的,彼此不知道对方的存在,两者仅仅共享一个自定义事件的名称. 在Nodejs中通过EventEmitte

  • JavaScript选择排序算法原理与实现方法示例

    本文实例讲述了JavaScript选择排序算法原理与实现方法.分享给大家供大家参考,具体如下: 一.选择排序简介 冒泡排序.插入排序.选择排序合称为简单排序.下面是选择排序的思想: 假设有一个数组a,我们想象成有一个班级名叫a班,现在全班随意排成一排,排头的位置是a[0],排尾的位置是a[a.length-1].但高矮顺序不是有序的,我们想从矮到高排,排头最矮,排尾最高. 选择排序是这样工作的: 第一轮: (1)a[1]位置队员与a[0]位置队员比较,如果比a[0]位置队员矮,就把a[1]的位置

  • JavaScript动画实例之粒子文本的实现方法详解

    1.粒子文本的实现原理 粒子文本的实现原理是:使用两张 canvas,一张是用户看不到的canvas1,用来绘制文本:另一张是用户看到的canvas2,用来根据canvas1中绘制的文本数据来生成粒子. 先在canvas1中用如下的语句绘制待显示的文本. ctx1.font = '100px PingFang SC'; ctx1.textAlign = 'center'; ctx1.baseline = 'middle'; ctx1.fillText('Happy New Year',canva

  • JavaScript动画函数封装详解

    目录 一.动画函数原理 二.动画函数简单封装 三. 动画函数给不同元素记录不同定时器 四.缓动效果原理 五. 动画函数在多个目标值之间移动 六.动画函数添加回调函数 一.动画函数原理 核心原理:通过定时器setInterval() 不断移动盒子位置. 实现步骤: 获得盒子当前位置 让盒子在当前位置加上1个移动距离 利用定时器不断重复这个操作 加一个结束定时器的条件 注意此元素需要添加定位,才能使用element.style.left 如下所示: 给定一个盒子,让其慢慢移动到300px的位置. 代

  • no-bundle 构建原理浅析

    目录 为什么需要构建工具? 什么是无包构建 基于浏览器的 JS 模块加载功能 HTML 中的 Script 引用注意点: 模块内依赖的引用 无包构建工具的介绍: Vite Vite对导入模块的解析 对 HTML 文件的预处理 对外部依赖包的解析 对 CSS 文件的解析 Vite 中的其他辅助功能 Vite 的使用限制 Snowpack 与 Vite 相同的功能点 与 Vite 的差异点 无包构建 VS 打包构建 无包构建的优点 无包构建的缺点 为什么需要构建工具? 处理其他类型文件使其能被浏览器

  • Javascript动画效果(1)

    前面我们介绍了Javascript的回到顶部效果,今天呢,我们对Javascript动画做进一步的研究.在这篇博文中我们只介绍简单的匀速运动.简单的缓冲运动和简单的多物体运动后面我们还会介绍任意值变化的运动.链式运动.同时运动,同时我们还会简单的封装一个运动插件并且还会将Javascript方法和jquery方法进行比较. 1.简单的匀速运动 下面我们介绍一个demo,鼠标移入,动画向右移动(即隐藏部分显示):鼠标离开,动画向左运动(继续隐藏)整个过程都是匀速的.有了前面回到顶部效果作为基础,这

  • javascript原型继承工作原理和实例详解

    先为大家分享JS原型继承实例,供大家参考,具体内容如下 一.JS原型继承 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>JS原型继承</title> </head> <body> <!--原型继承--> <script type="text/javascript"> //cl

随机推荐