requestAnimationFrame的理解

原文链接

在Web应用中,实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout/ setInterval 来实现,css3 可以使用 transition和 animation 来实现,html5 中的 canvas 也可以实现。除此之外,html5 还提供一个专门用于请求动画的API,那就是 requestAnimationFrame。

setTimeout/ setInterval 的显著缺陷就是设定的时间并不精确,它们只是在设定的时间后将相应任务添加到任务队列中,而任务队列中如果还有前面的任务尚未执行完毕,那么后添加的任务就必须等待,这个等待的时间造成了原本设定的动画时间间隔不准。

requestAnimationFrame的到来就是解决这个问题的 ,它采用的是系统时间间隔(约16.7ms),保持最佳绘制效果与效率,使各种网页动画有一个统一的刷新机制,从而节省系统资源,提高系统性能。

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,
并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。

优势

CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。
函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。一个刷新间隔内函数执行多次时没有意义的,因为显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来。

写法

window.requestAnimationFrame(callback);

callback:下一次重绘之前更新动画帧所调用的函数(即上面所说的回调函数)。回调函数会被传入DOMHighResTimeStamp参数(时间戳,十进制数,单位是毫秒,最小精度为1ms),DOMHighResTimeStamp指示当前被 requestAnimationFrame()排序的回调函数被触发的时间。
返回值是个非零值( long 整数),请求 ID ,是回调列表中唯一的标识。可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。

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

注意:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()

取消动画函数

cancelAnimationFrame(handlerId)复制代码
	var a=0
		function f(){
    
    		
		 if (a < 99) {
    
    
		 	a++
			console.log(a)
		   	requestAnimationFrame(f)
		  }else{
    
    
		  cancelAnimationFrame(f)
		  }
	}

requestAnimationFrame(f)

大量数据渲染

const total=100000//十万条数据
	const once=20;//每次插入20条
	const loopCount=Math.ceil(total/once);//需要插入的次数
	let countRender=0;//渲染次数
	
	const ul=document.querySelector('ul')
	
	function add(){
    
    //添加数据方法
		const fragment=document.createDocumentFragment()//创建虚拟dom节点
	for(let i=0;i<once;i++){
    
    
		const li=document.createElement('li')
		li.innerHTML=1
		fragment.appendChild(li)		
	}
	ul.appendChild(fragment)
	
	countRender++//渲染次数增加
	loop()//循环添加 递归
	}
	
	function loop(){
    
    
		if(countRender<loopCount){
    
    //当次数小于总次数时添加
			window.requestAnimationFrame(add)
		}
	}
	loop()

createDocumentFragment

1.createDocumentFragment()方法,是用来创建一个虚拟的节点对象,或者说,是用来创建文档碎片节点。它可以包含各种类型的节点,在创建之初是空的。
它有一个很实用的特点,当请求把一个DocumentFragment节点插入文档树时,插入的不是DocumentFragment自身,而是它的所有子孙节点,即插入的是括号里的节点,占位符。
当需要添加多个dom元素时,如果先将这些元素添加到DocumentFragment中,再统一将DocumentFragment添加到页面,会减少页面渲染dom的次数,效率会明显提升。

createDocumentFragment()方法和createElement()方法的区别:

  • createElement创建的元素可以重复操作,添加之后就算从文档里面移除依旧归文档所有,可以继续操作,但是createDocumentFragment创建的元素是一次性的,添加之后再就不能操作了(说明:这里的添加并不是添加了新创建的片段,因为上面说过,新创建的片段在文档内是没有对应的标签的,这里添加的是片段的所有子节点)。
  • 通过createElement新建元素必须指定元素tagName,因为其可用innerHTML添加子元素。通过createDocumentFragment则不必。
  • createElement创建的元素可以使用innerHTML,createDocumentFragment创建的元素使用innerHTML并不能达到预期修改文档内容的效果,只是作为一个属性而已。两者的节点类型完全不同,createElement创建的是元素节点,节点类型为1,createDocumentFragment创建的是文档碎片,节点类型是11。并且createDocumentFragment创建的元素在文档中没有对应的标记,因此在页面上只能用js中访问到。

猜你喜欢

转载自blog.csdn.net/weixin_51109349/article/details/125373798