JavaScript中的该如何[更好的]做动效
在用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中的该如何[更好的]做动效的更多相关文章
- JavaScript中该如何[更好的]做动效
在用js写动画的时候,无非使用 setTimeout/setInterval 或者 requestAnimationFrame 来处理动画(在jquery的代码里也是这么干的),本文主要为了记录下两者 ...
- javascript中的this与函数讲解
前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码, ...
- 更优雅的方式: JavaScript 中顺序执行异步函数
火于异步 1995年,当时最流行的浏览器--网景中开始运行 JavaScript (最初称为 LiveScript). 1996年,微软发布了 JScript 兼容 JavaScript.随着网景.微 ...
- JavaScript 中的数据类型
Javascript中的数据类型有以下几种情况: 基本类型:string,number,boolean 特殊类型:undefined,null 引用类型:Object,Function,Date,Ar ...
- 掌握javascript中的最基础数据结构-----数组
这是一篇<数据结构与算法javascript描述>的读书笔记.主要梳理了关于数组的知识.部分内容及源码来自原作. 书中第一章介绍了如何配置javascript运行环境:javascript ...
- 前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型
前端开发:面向对象与javascript中的面向对象实现(二)构造函数与原型 前言(题外话): 有人说拖延症是一个绝症,哎呀治不好了.先不说这是一个每个人都多多少少会有的,也不管它究竟对生活有多么大的 ...
- javascript中的变量作用域以及变量提升
在javascript中, 理解变量的作用域以及变量提升是非常有必要的.这个看起来是否很简单,但其实并不是你想的那样,还要一些重要的细节你需要理解. 变量作用域 “一个变量的作用域表示这个变量存在的上 ...
- 理解JavaScript中的“this”
对于javascript的初学者来说,一般对“this”关键字都感到非常迷惑.本文的目的旨在让你全面的了解“this”,理解在每一个情景下如何使用“this”,希望通过本文,可以帮助同学们不在害怕“t ...
- 详解Javascript中正则表达式的使用
正则表达式用来处理字符串特别好用,在JavaScript中能用到正则表达式的地方有很多,本文对正则表达式基础知识和Javascript中正则表达式的使用做一个总结. 第一部分简单列举了正则表达式在Ja ...
随机推荐
- 原生JavaScript实现焦点图轮播
不管是高校的网站还是电商的页面,焦点图的切换和轮播应该是一项不可或缺的应用.今天把焦点图轮播制作的技术要点做下笔记,以供日后查看. 一.结构层(HTML) 焦点图的HTML结构很简单,就是一个父容器( ...
- Spring Ioc介绍和Bean的实例化
一.IoC:Inverse of Control 控制反转 // 依赖注入 Dependency Injection 控制:某一接口具体实现类的选择权 反转:从调用者中移除控制权,转交第三方 ...
- asp.net core源码飘香:Logging组件
简介: 作为基础组件,日志组件被其他组件和中间件所使用,它提供了一个统一的编程模型,即不需要知道日志最终记录到哪里去,只需要调用它即可. 使用方法很简单,通过依赖注入ILogFactory(Creat ...
- 爬虫入门系列(三):用 requests 构建知乎 API
爬虫入门系列目录: 爬虫入门系列(一):快速理解HTTP协议 爬虫入门系列(二):优雅的HTTP库requests 爬虫入门系列(三):用 requests 构建知乎 API 在爬虫系列文章 优雅的H ...
- socket编程之 select、poll、kqueue、epoll
原生API select int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct tim ...
- .NET 二维码生成(ThoughtWorks.QRCode)【转发jiangys】
.NET 二维码生成(ThoughtWorks.QRCode) 2015-06-21 22:19 by jiangys, 3790 阅读, 8 评论, 收藏, 编辑 引用ThoughtWorks.QR ...
- bzoj2876 [Noi2012]骑行川藏
Description 蛋蛋非常热衷于挑战自我,今年暑假他准备沿川藏线骑着自行车从成都前往拉萨.川藏线的沿途有着非常美丽的风景,但在这一路上也有着很多的艰难险阻,路况变化多端,而蛋蛋的体力十分有限,因 ...
- 搭建自己的Git服务器
前言: GitHub是一个免费托管开源代码的远程仓库,使用起来即方便又安全,但在国内有时访问巨慢,原因你懂得.还有一些公司和个人视源码如生命,既不想开源代码又不想给Github交保护费.这时搭建一个自 ...
- JS基础——入门必备
·首先,来简单的说一下,JS是啥,JS是JavaScript的简写,是 基于浏览器的 基于对象的 事件驱动 脚本语言 ·那么JS有什么用呢?ta可以实现: 表单验证 添加动画效果 动态更改页面内容 A ...
- jQuery修炼心得-DOM节点的删除
要移除页面上节点是开发者常见的操作,jQuery提供了几种不同的方法用来处理这个问题. 1.empty empty 顾名思义,清空方法,但是与删除又有点不一样,因为它只移除了 指定元素中的所有子节点. ...