requestAnimationFrame学习笔记

最近工作中经常遇到动画的情况,之前常用的方法使用setTimeout或setInterval实现,但随着应用的越来越复杂,性能方面就会降低。所以选择使用requestAnimationFrame来实现相同效果。本文简单记录使用rAF的方法。


requestAnimationFrameHTML5中提供的动画API,简称rAF,即请求动画帧。可以让浏览器优化并行的动画动作,更合理的重新排列动作序列,并把能够合并的动作放在一个渲染周期内完成,从而呈现出更流畅的动画效果。说rAF之前先来简单了解与之相关的几个概念。

屏幕刷新频率

屏幕的刷新频率可在电脑中“高级显示设置”中查看,一般为60Hz,就是说,屏幕静置情况下,显示器会以每秒60次的频率不断更新屏幕上的图像,因为人的“视觉停留效应”,并感觉不到变化或者抖动,看到的仍是一幅幅连续的画面,其实这中间间隔时间是16.7ms(即1000/60)。
显示器刷新频率我们知道动画的本质就是让人看到图像在连贯、平滑地变动,那根据上面我们知道的,16.7ms屏幕刷新一次,在刷新时让图像移动1px,这样在视觉效果上便形成了动画。

setTimeout

从上面的介绍,我们可以了解,setTimeout其实就是通过一个时间间隔来不断更新图像形成的动画效果。但setTimeout在某些机型或复杂应用中会出现卡顿现象,也就是常说的“丢帧”。这事因为setTimeout只能设置固定的时间间隔,而不同的屏幕、机型会有不同的分辨率,而且setTimeout任务是被放进异步队列中的,所以实际执行时间会比设定时间晚一点。这些原因就导致了setTimeout动画的卡顿现象。

requestAnimationFrame

知道了setTimeout的缺点,rAF的出现就顺理成章了。rAF的回调函数执行时机由系统决定,也就是说,系统每次绘制前都会主动调用rAF中的回调函数,如果系统绘制频率是60Hz,那回调函数就是16.7ms被执行一次,如果系统绘制频率是75Hz,那么这个时间间隔就是1000/75=13.3ms,这样就保证回调函数在每次绘制中间都能执行一次,就不会出现丢帧的现象,也不会有卡顿的问题。
这个API的使用方法也很简单,如下:

var progress = 0;
//回调函数
function render() {
  progress += 1; //修改图像的位置
  if (progress < 100) {
    //在动画没有结束前,递归渲染
    window.requestAnimationFrame(render);
  }
}
//第一帧渲染
window.requestAnimationFrame(render);

优雅降级

因为rAF是HTML5提供的新API,目前还存在兼容性问题,所以需要对其进行优雅降级处理,这里提供网上的一些比较成熟的解决方案:

;(function () {
	var lastTime = 0
	var vendors = ['ms', 'moz', 'webkit', 'o']
	for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
		window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']
		window.cancelAnimationFrame =
			window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame']
	}

	if (!window.requestAnimationFrame)
		window.requestAnimationFrame = function (callback, element) {
			var currTime = new Date().getTime()
			var timeToCall = Math.max(0, 16 - (currTime - lastTime))
			var id = window.setTimeout(function () {
				callback(currTime + timeToCall)
			}, timeToCall)
			lastTime = currTime + timeToCall
			return id
		}

	if (!window.cancelAnimationFrame)
		window.cancelAnimationFrame = function (id) {
			clearTimeout(id)
		}
})()

以上来自tweenjs提供的RequestAnimationFrame.js https://github.com/tweenjs/tween.js/blob/master/examples/js/RequestAnimationFrame.js

实例

下面通过一个简单的例子来演示rAF的用法。
在html内设置一个圆,然后让其动起来。

<!DOCTYPE html>
<html>
<head>
  <title>rAF</title>
  <style>
    * {margin: 0;padding: 0}
    .box {width: 100px;height: 100px;border-radius: 100%;background: #f00;position: absolute;left: 0;top: 0}
  </style>
</head>
<body>
  <div class="box" id="box"></div>
</body>
</html>

简单的方式如下:

var box = document.getElementById('box')
var flag = false
var left = 0
function render() {
  if (flag) {
    if (left >= 100) {
      flag = false
    }
    box.style.left = `${left++}px`
  } else {
    if (left <= 0) {
      flag = true
    }
    box.style.left = `${left--}px`
  }
}
(function animloop() {
  render()
  window.requestAnimationFrame(animloop)
})()

以上便可让html中的圆形左右动起来。
如果想让动画停下来需要怎么处理呢,是否可以像clearInterval这样的方式呢?其实rAF提供了cancelAnimationFrame方法来停止动画处理,requestAnimationFrame默认会返回一个id,将id传入cancelAnimationFrame中便可达到效果。
修改以上代码:

var box = document.getElementById('box')
var flag = false
var left = 0
var rAFId = ''
function render() {
  if (flag) {
    if (left >= 100) {
      flag = false
    }
    box.style.left = `${left++}px`
  } else {
    if (left <= 0) {
      flag = true
    }
    box.style.left = `${left--}px`
  }
}
(function animloop() {
  render()
  rAFId = window.requestAnimationFrame(animloop)
  if (left == 50) {
    cancelAnimationFrame(rAFId)
  }
})()

这样在圆运动到距左侧50px的时候就停下来了。

自己的学习笔记,如果你刚好看到,希望也能帮着你,如有不正确的地方欢迎多多指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值