在用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

<!-- 为了更好的看到滚动效果及测滚动是否平滑,我们用某度的大图扔页面上 -->
<img src="./来自百度壁纸的大图" />
<img src="./来自百度壁纸的大图" />
<img src="./来自百度壁纸的大图" /> <div id="scrollTop_1000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:50px;right:50px;text-align: center">1000</div>
<div id="scrollTop_3000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:80px;right:50px;text-align: center">3000</div>

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

<!-- 为了更好的看到滚动效果及测滚动是否平滑,我们用某度的大图扔页面上 -->
<img src="./来自百度壁纸的大图" />
<img src="./来自百度壁纸的大图" />
<img src="./来自百度壁纸的大图" /> <div id="scrollTop_1000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:50px;right:50px;text-align: center">1000</div>
<div id="scrollTop_3000" style="width: 60px;height: 20px;background: #fff;position: fixed; bottom:80px;right:50px;text-align: center">3000</div>

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

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

JavaScript中的该如何[更好的]做动效的更多相关文章

  1. JavaScript中该如何[更好的]做动效

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

  2. javascript中的this与函数讲解

    前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码, ...

  3. 更优雅的方式: JavaScript 中顺序执行异步函数

    火于异步 1995年,当时最流行的浏览器--网景中开始运行 JavaScript (最初称为 LiveScript). 1996年,微软发布了 JScript 兼容 JavaScript.随着网景.微 ...

  4. JavaScript 中的数据类型

    Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...

  5. 掌握javascript中的最基础数据结构-----数组

    这是一篇<数据结构与算法javascript描述>的读书笔记.主要梳理了关于数组的知识.部分内容及源码来自原作. 书中第一章介绍了如何配置javascript运行环境:javascript ...

  6. 前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型

    前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型 前言(题外话): 有人说拖延症是一个绝症,哎呀治不好了.先不说这是一个每个人都多多少少会有的,也不管它究竟对生活有多么大的 ...

  7. javascript中的变量作用域以及变量提升

    在javascript中, 理解变量的作用域以及变量提升是非常有必要的.这个看起来是否很简单,但其实并不是你想的那样,还要一些重要的细节你需要理解. 变量作用域 “一个变量的作用域表示这个变量存在的上 ...

  8. 理解JavaScript中的“this”

    对于javascript的初学者来说,一般对“this”关键字都感到非常迷惑.本文的目的旨在让你全面的了解“this”,理解在每一个情景下如何使用“this”,希望通过本文,可以帮助同学们不在害怕“t ...

  9. 详解Javascript中正则表达式的使用

    正则表达式用来处理字符串特别好用,在JavaScript中能用到正则表达式的地方有很多,本文对正则表达式基础知识和Javascript中正则表达式的使用做一个总结. 第一部分简单列举了正则表达式在Ja ...

随机推荐

  1. Python练习册 第 0013 题: 用 Python 写一个爬图片的程序,爬 这个链接里的日本妹子图片 :-),(http://tieba.baidu.com/p/2166231880)

    这道题是一道爬虫练习题,需要爬链接http://tieba.baidu.com/p/2166231880里的所有妹子图片,点进链接看一下,这位妹子是日本著名性感女演员--杉本由美,^_^好漂亮啊,赶紧 ...

  2. select 训练

    --1.查询"c001"课程比"c002"课程成绩高的所有学生的学号:SELECT b.sno FROM (SELECT * FROM sc WHERE cno ...

  3. ReentrantLock 以及 AQS 实现原理

    什么是可重入锁?       ReentrantLock是可重入锁,什么是可重入锁呢?可重入锁就是当前持有该锁的线程能够多次获取该锁,无需等待.可重入锁是如何实现的呢?这要从ReentrantLock ...

  4. 1.6 在WHERE子句中引用取别名的列

    如下查询,会抛出错误:mysql> select sal as salary, comm as commission from emp where salary < 5000;ERROR ...

  5. aos.js超赞页面滚动元素动画jQuery动画库

    插件描述:aos.js 是一款效果超赞的页面滚动元素动画jQuery动画库插件.该动画库可以在页面滚动时提供28种不同的元素动画效果,以及多种easing效果.在页面往回滚动时,元素会恢复到原来的状态 ...

  6. ue4竖排文本显示

    最近发现中国风游戏中,经常会遇到旁白文字竖着显示的需求. 于是我首先找了找控件蓝图中的text有没有相关类似横竖文本框的选项,然而并无所获. 突然间灵机一动! 竖着显示不就是每个字一换行嘛! 说干就干 ...

  7. Excel 中使用sql语句查询

    将Excel连接Oracle数据库 Excel选项板中"数据"—"自其他来源"下拉菜单中有有个可以连接其它数据库的选项"来自数据连接向导"和 ...

  8. Apache Ranger对HDFS的访问权限控制的原理分析(一)

    介绍 Aapche Ranger是以插件的形式集成到HDFS中,由Ranger Admin管理访问策略,Ranger插件定期轮询Admin更新策略到本地,并根据策略信息进行用户访问权限的判定.Rang ...

  9. Hadoop - 操作练习之单机配置 - Hadoop2.8.0/Ubuntu16.04

    系统版本 anliven@Ubuntu1604:~$ uname -a Linux Ubuntu1604 4.8.0-36-generic #36~16.04.1-Ubuntu SMP Sun Feb ...

  10. 使用虚拟机CentOS7部署CEPH集群

    第1章   CEPH部署 1.1  简单介绍 Ceph的部署模式下主要包含以下几个类型的节点 Ø CephOSDs: A Ceph OSD 进程主要用来存储数据,处理数据的replication,恢复 ...