百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

性能优化之通俗易懂学习requestAnimationFrame和使用场景举例

toyiye 2024-04-04 11:27 32 浏览 0 评论

一项新技术新的技术方案的提出,一定是为了解决某个问题的,或者是对某种方案的优化,比如window.requestAnimationFrame这个api...

requestAnimationFrame官方介绍

requestAnimationFrame用处概述

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

官方文档对应截图

官方文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame

大致看了以后,我们可以知道:

requestAnimationFrame这个api主要是用来做动画的。

requestAnimationFrame这个api主要是用来做动画的。

requestAnimationFrame这个api主要是用来做动画的。

其实顾名思义,我们翻译这个英文单词,也能大致明白。request(请求)Animation(动画)Frame(帧)

关于前端动画的两个问题:

1.前端动画方案有哪些?

2.为何偏偏要使用这个新的api来做动画(或者说这个api较之前做动画的方式优点有哪些)?

1.前端动画方案有哪些?

主要分类为css动画js动画,如下细分:

  • css动画
  • transition过渡动画
  • animation直接动画(搭配@keyframes
  • js动画
  • setIntervalsetTimeout定时器(比如不停地更改dom元素的位置,使其运动起来)
  • canvas动画,搭配js中的定时器去运动起来(canvas只是一个画笔,然后我们通过定时器会使用这个画笔去画画-动画)
  • requestAnimationFrame动画(js动画中的较好方案)

另有svg动画标签,不过工作中这种方式是比较少的,这里不赘述

2.为何偏偏要使用这个新的api来做动画(或者说这个api较之前做动画的方式优点有哪些)?

在工作中,做动画最优的方案无疑是css动画,但是某些特定场景下,css动画无法实现我们所需要的需求,此时,我们就要考虑使用js去做动画了

canvas动画本质也是定时器动画

使用定时器动画干活,实际上是可以的,但是存在一个最大的问题,就是动画会抖动动画会抖动动画会抖动,体验效果不是非常好。

而,使用requestAnimationFrame去做动画,就不会抖动就不会抖动就不会抖动

这里笔者写一个demo动画(分别是上述两种方式实现dom元素向右平移)给大家看一下,就知道具体的区别。我们先看一下效果图:(红色dom是定时器实现、绿色domrequestAnimationFrame实现)

因为笔者的gif录制软件的问题,看着都有点卡,实际上,大家把下方代码复制一份跑起来看的话,会发现定时器动画在微微颤抖,而requestAnimationFrame动画却稳如老狗

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>requestAnimationFrame_yyds</title>
	<style>
		body {
			box-sizing: border-box;
			background-color: #ccc;
		}

		.box1,
		.box2 {
			position: absolute;
			width: 160px;
			height: 160px;
			line-height: 160px;
			text-align: center;
			color: #fff;
			font-size: 13px;
		}

		.box1 {
			top: 40px;
			background: red;
		}

		.box2 {
			top: 210px;
			background: green;
		}
	</style>
	</style>
</head>

<body>
	<button class="btn"> let's go!</button>
	<div class="box1">定时器动画</div>
	<div class="box2">请求动画帧</div>
	<script>
		// 动画思路:不断修改dom元素的left值,使其运动起来(动画)
		let box1 = document.querySelector('.box1')
		let box2 = document.querySelector('.box2')

		// setInterval定时器方式
		function setIntervalFn() {
			let timer = null
			box1.style.left = '0px'
			timer = setInterval(() => {
				let leftVal = parseInt(box1.style.left)
				if (leftVal >= 720) {
					clearInterval(timer)
				} else {
					box1.style.left = leftVal + 1 + 'px'
				}
			}, 17)
		}

		// requestAnimationFrame请求动画帧方式
		function requestAnimationFrameFn() {
			let timer = null // 可注掉
			box2.style.left = '0px'
			function callbackFn() {
				let leftVal = parseInt(box2.style.left)
				if (leftVal >= 720) {
					// 不再继续递归调用即可,就不会继续执行了,下面这个加不加都无所谓,因为影响不到
					// cancelAnimationFrame取消请求动画帧,用的极少,看下,下文中的回到顶部组件
					// 大家会发现并没有使用到这个api(这样写只是和clearInterval做一个对比)
					// 毕竟,正常情况下,requestAnimationFrame会自动停下来
					cancelAnimationFrame(timer) // 可注掉(很少用到)
				} else {
					box2.style.left = leftVal + 1 + 'px'
					window.requestAnimationFrame(callbackFn)
				}
			}
			window.requestAnimationFrame(callbackFn)
		}

		// 动画绑定
		let btn = document.querySelector('.btn')
		btn.addEventListener('click', () => {
			setIntervalFn()
			requestAnimationFrameFn()
		})
	</script>
</body>

</html>

Chrome浏览器查看当前帧数命令:1. F12打开控制台2. command + shift + p调出输入面板3. 在Run输入框中输入:Show frames per second(FPS) meter回车即可

通过上述的例子,我们可以回答这个问题了:

  • 面试官问:requestAnimationFrame比定时器好在哪里?
  • 候选人答:好在比较稳定,动画不卡顿
  • 面试官说:你回去等通知吧...

所以在这里,我们还要顺带延伸一下,为什么定时器会卡,而requestAnimationFrame不会卡。在说这个问题之前,这里再提一下,requestAnimationFrame的语法规则

requestAnimationFrame的语法规则

一言以蔽之:requestAnimationFramejs中的setTimeout定时器函数基本一致,不过setTimeout可以自由设置间隔时间,而requestAnimationFrame的间隔时间是由浏览器自身决定的,大约是17毫秒左右

1.requestAnimationFrame我们可以在控制台输入window,然后展开查看其身上的属性,就能找到了,如下图:

2.由上图我们可以看到,requestAnimationFrame本质上是一个全局window对象上的一个属性函数,函数是要被执行的,要被调用的。所以我们使时,直接:window.requestAnimationFrame(callBack)即可。

3.和定时器一样其接收的参数callback也是一个函数,即下一次重绘之前更新动画帧所调用的函数,即在这个函数体中,我们可以写对应的逻辑代码(和定时器类似)

4.requestAnimationFrame也有返回值,返回值是一个整数,主要是定时器的身份证标识,可以使用window.cancelAnimationFrame()来取消回调函数执行,相当于定时器中的clearTimeout()

5.二者也都是只执行一次,想要继续执行,做到类似setInterval的效果,需要写成递归的形式(上述案例中也提到了)

为什么定时器会卡,而requestAnimationFrame不会卡

为什么定时器会卡

  • 我们在手机或者电脑显示屏上看东西时,显示屏会默默的不停地干活(刷新画面)
  • 这个刷新值得是每秒钟刷新次数,普通显示器的刷新率约为60Hz(每秒刷新60次),高档的有75Hz、90Hz、120Hz、144Hz等等
  • 刷新率次数越高,显示器显示的图像越清晰、越流畅、越丝滑
  • 不刷新就是静态的画面,刷新比较低就是卡了PPT的感觉
  • 动画想要丝滑流畅,需要卡住时间点进行代码操作(代码语句赋值、浏览器重绘)
  • 所以只需要每隔1000毫秒的60分之一(60HZ)即约为17毫秒,进行一次动画操作即可
  • 只要卡住这个17毫秒,每隔17毫秒进行操作,就能确保动画丝滑
  • 但是定时器的回调函数,会受到js的事件队列宏任务、微任务影响,可能设定的是17毫秒执行一次,但是实际上这次是17毫秒、下次21毫秒、再下次13毫秒执行,所以并不是严格的卡住了这个60HZ的时间
  • 没有在合适的时间点操作,就会出现:类似这样的情况:不变不变不变...
  • 于是就出现了,绘制不及时的情况,就会有抖动的出现(以上述案例,位置和时间没有线性对应更新变化导致看起来抖动)

js执行代码是很快的,可能不到一毫秒,大家可以使用相应console的api去测试,如下:

console.time()
let box1 = document.querySelector('.box1')
box1.style.left = '100px'
console.timeEnd()

// js执行耗时结果:default: 0.044189453125 ms

为何requestAnimationFrame不会卡

requestAnimationFrame能够做到,精准严格的卡住显示器刷新的时间,比如普通显示器60HZ它会自动对应17ms执行一次,比如高级显示器120HZ,它会自动对应9ms执行一次。

当然requestAnimationFrame只会执行一次,想要使其多次执行,要写成递归的形式。上述案例也给出了递归写法

至于为何requestAnimationFrame能够卡住时间,其底层原理又是啥?本文暂且按下不表。

所以,这就是requestAnimationFrame的好处。

所以,上述内容验证了:一项新技术新的技术方案的提出,一定是为了解决相关的问题的。

所以,window.requestAnimationFrame这个api就是解决了定时器不精准的问题的。

这就是其产生的原因。

requestAnimationFrame应用场景举例-回到顶部组件

比如:回到顶部组件,就是使用requestAnimationFrame实现的。

下面是笔者封装的回到顶部组件效果图和代码

效果图:

也可以去笔者的网站上去看效果哦:http://ashuai.work:8888/#/myBack

代码:

<template>
  <transition name="fade-transform">
    <div
      v-show="visible"
      class="backWrap"
      :style="{
        bottom: bottom + 'px',
        right: right + 'px',
      }"
      @click="goToTop"
    >
      <slot></slot>
    </div>
  </transition>
</template>

<script>
export default {
  name: "myBack",
  props: {
    bottom: {
      type: Number,
      default: 72,
    },
    right: {
      type: Number,
      default: 72,
    },
    // 回到顶部出现的滚动高度位置
    showHeight: {
      type: Number,
      default: 240,
    },
    // 拥有滚动条的那个dom元素的id或者class,用于下方选中操作更改滚动条滚动距离
    scrollBarDom: String,
  },
  data() {
    return {
      visible: false,
      scrollDom: null,
    };
  },
  mounted() {
    if (document.querySelector(this.scrollBarDom)) {
      this.scrollDom = document.querySelector(this.scrollBarDom);
      // 不用给window绑定监听滚动事件,给对应滚动条元素绑定即可
      this.scrollDom.addEventListener("scroll", this.isShowGoToTop, true);
    }
  },
  beforeDestroy() {
    // 最后要解除监听滚动事件
    this.scrollDom.removeEventListener("scroll", this.isShowGoToTop, true);
  },
  methods: {
    isShowGoToTop() {
      // 获取滚动的元素,即有滚动条的那个元素
      if (this.scrollDom.scrollTop > 20) {
        this.visible = true;
      } else {
        this.visible = false;
      }
    },
    goToTop() {
      // 获取滚动的元素,即有滚动条的那个元素
      let scrollDom = document.querySelector(this.scrollBarDom);
      // 获取垂直滚动的距离,看看滚动了多少了,然后不断地修改滚动距离直至为0
      let scrollDistance = scrollDom.scrollTop;

      /**
       * window.requestAnimationFrame兼容性已经可以了,正常都有的
       * */
      if (window.requestAnimationFrame) {
        let fun = () => {
          scrollDom.scrollTop = scrollDistance -= 36;
          if (scrollDistance > 0) {
            window.requestAnimationFrame(fun); // 只执行一次,想多次执行需要再调用
          } else {
            scrollDom.scrollTop = 0;
          }
        };
        window.requestAnimationFrame(fun);
        return;
      }

      /**
       * 没有requestAnimationFrame的话,就用定时器去更改滚动条距离,使之滚动
       * */
      let timer2 = setInterval(() => {
        scrollDom.scrollTop = scrollDistance -= 36;
        if (scrollDistance <= 0) {
          clearInterval(timer2);
          scrollDom.scrollTop = 0;
        }
      }, 17);
    },
  },
};
</script>

<style lang='less' scoped>
.backWrap {
  position: fixed;
  cursor: pointer;
  width: 42px;
  height: 42px;
  background: #9cc2e5;
  border-radius: 4px;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: all 0.5s;
}

// 过渡效果
.fade-transform-leave-active,
.fade-transform-enter-active {
  transition: all 0.36s;
}

.fade-transform-enter {
  opacity: 0;
  transform: translateY(-5px);
}
.fade-transform-leave-to {
  opacity: 0;
  transform: translateY(5px);
}
</style>

GitHub仓库地址:https://github.com/shuirongshuifu/elementSrcCodeStudy

类比学习reduce循环解决了forEach循环可能需要一个初始变量的问题

我们类比一下学习,比如既然有了forEach循环,为啥还又新推出一个reduce循环呢?

原因:某些场景下,reduce循环解决了forEach循环还需要再定义一个变量的问题。

似曾相识的感觉...

比如我们有一个需求,给一个数组求和。

forEach写法

let arr = [1, 3, 5, 7, 9]
function forEachFn(params) {
    let total = 0
    params.forEach((num) => {
        total = total + num
    })
    return total
}
let res1 = forEachFn(arr)
console.log(res1);

reduce写法

let arr = [1, 3, 5, 7, 9]
function reduceFn(params) {
    return params.reduce((temp, num) => {
        temp = temp + num
        return temp
    }, 0)
}
let res2 = reduceFn(arr)
console.log(res2);

通过上述两段代码,我们可以看到,reduce函数比forEach少写了一个total变量,千万不要小看这少写的东西,某些情况下,会节省很多的工作量呢!

一项新技术新的技术方案的提出,一定是为了解决某个问题的,或者是对某种方案的优化,比如xxx


相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码