博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript中该如何[更好的]做动效
阅读量:6348 次
发布时间:2019-06-22

本文共 4974 字,大约阅读时间需要 16 分钟。

在用js写动画的时候,无非使用 setTimeout/setInterval 或者 requestAnimationFrame 来处理动画(在jquery的代码里也是这么干的),本文主要为了记录下两者的区别及使用两者来实现动过程。

以实现一个简单的滚动到顶部为例

setInterval

setInterval() 方法重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间延迟。返回一个intervalID,可用于 cancelInterval 达到结束循环的效果。

setTimeout 和 setInterval 的实现基本没区别,一个是定时执行,一个是定时循环执行,前者加个自己调用自己就是后者了,下面主要以 setInterval 为代表

实现过程:

1.写个方法,该方法需要传入一个代表动画所需执行的时间的参数(如:滚动到顶部需要1000毫秒)

function doAnimate(duration){    return function(){        // do something    }}

2.取当前页面距顶部高度、滚动速度(以匀速为例)、写个开始动画的函数(为了给addEventListener绑事件传参,其实也可直接 dom.onclick = fn )

function doAnimate(duration){    return function(){        var start = document.documentElement.scrollTop;        var scrollSpeed = start/duration*(1000/60); // 以大多浏览器的刷新频率60帧(60Hz)为准  1秒60次的刷新        var timer;        var startTime = +new Date(); // 标记时间,仅供后面测效果用而已        function startAnimate(){            timer = setInterval(function () {                // do something            },1000/60)        }    }}

3.写滚动动画

function doAnimate(duration){    return function(){        var start = document.documentElement.scrollTop;        var scrollSpeed = start/duration*(1000/60); // 这里取到平均滚动距离,以大多浏览器的刷新频率60帧(60Hz)为准,1秒60次的刷新        var timer;        function startAnimate(){            timer = setInterval(function () {                document.documentElement.scrollTop = start < scrollSpeed ? start -= start : start -= scrollSpeed; // 当前该滚动到何处,如果距离顶部小于平均滚动距离,直接滚到scrollTop为0;如果大于,则取到的触发时高度以平均滚动距离递减                if(start === 0){                    clearInterval(timer);                }            },1000/60)        }    }}

4.写个很高的页面、给个div、加个click事件触发滚动回顶部

html

1000
3000

js

function doAnimate(duration){    return function(){        var start = document.documentElement.scrollTop;        var scrollSpeed = start/duration*(1000/60); // 这里取到平均滚动距离,以大多浏览器的刷新频率60帧(60Hz)为准,1秒60次的刷新        var timer;        function startAnimate(){            timer = setInterval(function () {                document.documentElement.scrollTop = start < scrollSpeed ? start -= start : start -= scrollSpeed; // 当前该滚动到何处,如果距离顶部小于平均滚动距离,直接滚到scrollTop为0;如果大于,则取到的触发时高度以平均滚动距离递减                if(start === 0){                    clearInterval(timer);                }            },1000/60)        }    }}document.getElementById('scrollTop_1000').addEventListener('click',doAnimate(1000),!1)document.getElementById('scrollTop_3000').addEventListener('click',doAnimate(3000),!1)

效果如图:

截图分别测了设置 duration 为1000和3000的滚动效果 

requestAnimationFrame

requestAnimationFrame() 方法告诉浏览器您希望执行动画,并请求浏览器调用指定的函数在下一次重绘之前更新动画。该方法将在重绘之前调用的回调作为参数。返回一个 requestID ,可用于 cancelAnimationFrame 达到取消 requestAnimationFrame 动画的效果。

实现思路如上,代码如下:

html

1000
3000

js

function doAnimate(duration){    return function(){        var start = document.documentElement.scrollTop;        // 获取实时时间        var nowTime = function(){            return +new Date        }        // 开始时间 用于计算动画运行时间和动画规定时间的百分比        var startTime = nowTime();        var animateId;        var startAnimate = function() {            animateId = requestAnimationFrame(toTop);        }        var stopAnimate = function() {            cancelAnimationFrame(animateId)        }        function toTop(){            // 剩下时间            var restTime = Math.max(0, duration - ( nowTime() - startTime))            var percent = restTime / duration || 0;            var changeStyle = function(value){                document.documentElement.scrollTop = value;            }            // 根本比例获取剩下的距离,也就是实时距离顶部的距离            var distance = start * percent;            if(!distance){                changeStyle(distance)                stopAnimate();            }else{                changeStyle(distance)                startAnimate();            }        }        startAnimate();    }}document.getElementById('scrollTop_1000').addEventListener('click',doAnimate(1000),!1)document.getElementById('scrollTop_3000').addEventListener('click',doAnimate(3000),!1)

效果如图:

没区别,没毛病,然而并没有和上面用同一张图...(其实打印下时间,会发现 setInterval 会是1000毫秒以内,大致在960-980毫秒之间,这个梗哪位大神可知???求解!!!)

两者的区别

requestAnimationFrame 会请求浏览器调用指定的函数在下一次重绘之前更新动画,所以开发者不用考虑频率/丢帧问题

setInterval 中,会因为浏览器显示频率和 JavaScript 单线程可能会引发阻塞的问题而导致丢帧(视觉应为动画不流畅)

requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,性能方面更出色

对于隐藏或者不可见的元素,requestAnimationFrame 将不会进行重绘或回流,这点可减少cpu,gpu及内存的负荷

setInterval 兼容一些老版本的浏览器(jquery保留这个应该也是为了兼容老版本浏览器...)

requestAnimationFrame 兼容图

顺便扔上jquery里animate的部分代码:

jQuery.fx.start = function() {    if ( !timerId ) {        timerId = window.requestAnimationFrame ?            window.requestAnimationFrame( raf ) :            window.setInterval( jQuery.fx.tick, jQuery.fx.interval );    }};jQuery.fx.stop = function() {    if ( window.cancelAnimationFrame ) {        window.cancelAnimationFrame( timerId );    } else {        window.clearInterval( timerId );    }    timerId = null;};

略显尴尬... 在我windows上和mac上也保留一些老版本的浏览器,测效果的结果简直蛋疼...看来兼容方面还是需要做处理的,天将降大任于 setInterval 啊    :-D

欢迎交流 欢迎指出各个问题~

 

转载地址:http://qfvla.baihongyu.com/

你可能感兴趣的文章
【实用】面对枯燥的源码,如何才能看得下去?
查看>>
智库说 | 徐远:数字时代的城市潜力
查看>>
《JSP极简教程》jsp c:forEach用法
查看>>
WebSocket详解(六):刨根问底WebSocket与Socket的关系
查看>>
用 Go 写一个轻量级的 ssh 批量操作工具
查看>>
网站设计之合理架构CSS 架构CSS
查看>>
OTP 22.0 RC3 发布,Erlang 编写的应用服务器
查看>>
D语言/DLang 2.085.1 发布,修复性迭代
查看>>
感觉JVM的默认异常处理不够好,既然不好那我们就自己来处理异常呗!那么如何自己处理异常呢?...
查看>>
Java 基础 之 算数运算符
查看>>
Windows下配置安装Git(二)
查看>>
一个最简单的基于Android SearchView的搜索框
查看>>
铁路开通WiFi“钱景”不明
查看>>
Nutanix领衔的超融合能带来新存储黄金时代吗?
查看>>
Facebook申请专利 或让好友及陌生人相互拼车
查看>>
电力“十三五”规划:地面光伏与分布式的分水岭
查看>>
美联社再告FBI:要求公开请黑客解锁iPhone花费
查看>>
三星电子出售希捷和夏普等四家公司股份
查看>>
任志远:当云计算遇上混合云
查看>>
思科联手发那科 用物联网技术打造无人工厂
查看>>