React-fiber基础之requestAnimationFrame和requestIdleCallback

news2025/1/15 16:10:27

屏幕刷新率

  • 目前大多数设备的屏幕刷新率是60次每秒
  • 浏览器渲染动画或者页面的每一帧的速率也需要根设备屏幕的刷新率保持一致
  • 页面是一帧一帧绘制出来的,当每秒的帧数(FPS)达到60,页面是流畅的,小于这个值,用户会感觉到卡顿
  • 每一帧的预算时间大概是16.6ms
  • 1s 60帧,所以每帧时间大概16.6ms,我们书写代码时力求不让每一帧工作量超过16.6

每帧浏览器都做了什么

  1. 阻塞输入事件(输入事件,触摸事件,鼠标滚动事件),非阻塞输入事件(点击,键盘事件)。因为和用户交互,优先级最高
  2. 定时器,js脚本,微任务
  3. 开始帧(每一帧的事件,如窗口resize事件,滚动,媒体查询事件)
  4. requestAnimateFrame
  5. layout布局,计算样式,更新布局
  6. 绘制paint,记录,合成图层等
  7. 空闲阶段(idle peroid)requestIdleCallback

requestAnimationFrame

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

执行时机是每一帧浏览器进行渲染之前,是一个高优的任务。

当你准备更新动画时你应该调用此方法。这将使浏览器在下一次重绘之前调用你传入给该方法的动画函数 (即你的回调函数)。

回调函数执行次数通常是每秒 60 次,但在大多数遵循 W3C 建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。为了提高性能和电池寿命,因此在大多数浏览器里,当requestAnimationFrame() 运行在后台标签页或者隐藏的<iframe> 里时,requestAnimationFrame() 会被暂停调用以提升性能和电池寿命。

也就是说,当我们切换浏览器的tab页面,或者浏览器窗口最小化以后,使用改api模拟的动画会暂停,直到当前页面再次出现在屏幕的可视区域。

回调函数会被传入DOMHighResTimeStamp参数,DOMHighResTimeStamp指示当前被 requestAnimationFrame() 排序的回调函数被触发的时间。在同一个帧中的多个回调函数,它们每一个都会接受到一个相同的时间戳,即使在计算上一个回调函数的工作负载期间已经消耗了一些时间。该时间戳是一个十进制数,单位毫秒,最小精度为 1ms(1000μs)。

通俗的说,该api会给传递的回调函数一个参数,该参数是当前页面渲染完成到当前函数执行时,已经过去了多少时间。这个参数的值在后续的每次渲染都会不断增加,因为浏览器渲染完毕以后,时间总是在增加。当然呢刷新页面以后会被重置。

demo:

<!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>Document</title>
  <style>
    div {
      height: 200px;
      width: 0;
      background-color: aqua;
      transition: all 0.1s;
    }
  </style>
</head>

<body>
  <div></div>
  <script>
    const div = document . querySelector('div')
    let width = 0
    const animationWidth = (time) => {
      width  += 10
      div.style.width = width  +  'px'
      div.innerHTML = (width  /  10)  +  '%'
      // 如果页面渲染了两秒时间过去了还没执行完 就不执行动画了       if (width < 1000 && time < 2000) requestAnimationFrame(animationWidth)
    }
    requestAnimationFrame(animationWidth)
  </script>
</body>

</html>

我们在回调函数中,再把当前的动画回调函数放入requestAnimationFrame中,会在下一次渲染页面前继续执行该动画函数

requestIdleCallback

window.requestIdleCallback() 方法插入一个函数,这个函数将在浏览器空闲时期被调用。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序。

在浏览器每次渲染页面时,我们会反馈(响应用户的输入),页面的拖拽,浏览器大小的改变,js主线程的任务,微任务宏任务,requestAnimationFrame回调,浏览器进行绘制,如果这些事情做完以后,还有剩余时间,我们认为这就是当前渲染帧的空闲时间,这个时间浏览器可以交给我们开发者(js主线程执行一些优先级不高的任务)。

可以这样认为,我们使用这个api了,那么浏览器在当前帧还有剩余时间,那么就会把执行权交给我们,但是我们也需要在剩余时间结束之前把执行权还给浏览器。

要记住,拿到执行权,然后在使用完毕以后,把执行权还给浏览器,这个是我们开发者和浏览器的 “君子协议”,你当然拿到执行权可以不交给浏览器了(比如我们进行一次死循环,那么很显然浏览器会卡死,没办法响应用户输入等)。

demo:

    const sleep = (time) => {
      const now = Date . now()
      while (Date . now() < now  +  time) { }
    }
    const worksQueue = [
      () => {
        console . log('任务A开始')
        sleep(20)
        console . log('任务A结束')
      },
      () => {
        console . log('任务B开始')
        sleep(20)
        console . log('任务B结束')
      },
      () => {
        console . log('任务C开始')
        sleep(20)
        console . log('任务C结束')
      },
      () => {
        console . log('任务D开始')
        sleep(20)
        console . log('任务D结束')
      },
      () => {
        console . log('任务E开始')
        sleep(20)
        console . log('任务E结束')
      }
    ]
    const doSomething = (idleDeadline) => {       // idleDeadline.timeRemaining() // 返回值是剩余时间       while (idleDeadline. timeRemaining() > 0 && worksQueue.length) {
        console . log(idleDeadline. timeRemaining())
        const work = worksQueue. shift()
        work()
      }
      // 还有任务       if(worksQueue.length) requestIdleCallback(doSomething)
    }
    requestIdleCallback(doSomething)

看打印结果,可以发现:

在这里插入图片描述

我们可以看出来,前面的剩余时间都很正常,但是中间出现了一个50,这个是为什么?

因为浏览器可以发现,我们页面已经长时间没有发生过交互了,而对于人眼来说,响应交互的时延小于在50ms,也就是一秒20帧,人不会感觉明显的卡顿,所以浏览器会多给我们一些时间来执行这些优先级不高的任务。

如果我们把任务执行时间延长,比如一个任务需要1000ms,会发生什么?

    const worksQueue = [
      () => {
        console . log('任务A开始')
        sleep(20)
        console . log('任务A结束')
      },
      () => {
        console . log('任务B开始')
        sleep(1000)
        console . log('任务B结束')
      },
      () => {
        console . log('任务C开始')
        sleep(20)
        console . log('任务C结束')
      },
      () => {
        console . log('任务D开始')
        sleep(1000)
        console . log('任务D结束')
      },
      () => {
        console . log('任务E开始')
        sleep(20)
        console . log('任务E结束')
      }
    ]

在这里插入图片描述

很明显,浏览器都会提醒我们,执行的时候时间超时了。

requestIdleCallback第二个参数

但是,因为这个任务是非常低优先级的,可能任务在多次渲染后,仍没有机会很执行。

所以我们还可以传递一个参数,timeout指定超时时间,如果超时了(太长时间还没执行该任务),那么我们也去执行这些低优先级的任务。

如果didTimeout为ture,表明当前任务正在执行,且上次因为超时没有执行该任务

const sleep = (time) => {
      const now = Date . now()
      while (Date . now() < now  +  time) { }
    }
    const worksQueue = [
      () => {
        console . log('任务A开始')
        sleep(20)
        console . log('任务A结束')
      },
      () => {
        console . log('任务B开始')
        sleep(20)
        console . log('任务B结束')
      },
      () => {
        console . log('任务C开始')
        sleep(20)
        console . log('任务C结束')
      },
      () => {
        console . log('任务D开始')
        sleep(20)
        console . log('任务D结束')
      },
      () => {
        console . log('任务E开始')
        sleep(20)
        console . log('任务E结束')
      }
    ]
    const doSomething = (idleDeadline) => {
      // idleDeadline.didTimeout // 是否过期 过期了就强制执行该任务       // idleDeadline.timeRemaining() // 返回值是剩余时间       while ((idleDeadline. timeRemaining() > 0 || idleDeadline.didTimeout) && worksQueue.length) {
        console . log(idleDeadline.didTimeout)
        console . log(idleDeadline. timeRemaining())
        const work = worksQueue. shift()
        work()
      }
      // 还有任务       if (worksQueue.length) requestIdleCallback(doSomething, { timeout: 10 })
    }
    requestIdleCallback(doSomething, { timeout: 100 })

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/24147.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

公众号免费查题接口

公众号免费查题接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击…

G1D19-DPCS KEAttacKG复现

今天先来看DP 一、DP 1044初稿完成啦~~ 创作与表达的过程总是令人心情愉悦&#xff01; 内容很多&#xff0c;所以效率还算可以啦~但是如果能把工作流程定义地更加清楚的话&#xff0c;效率应该还可以再高一点&#xff01;&#xff01;&#xff01; 二、CS KE 我发现自己网…

Layer2之争:短期看Optimism 长期看zkSync?

今天来说说 Layer2 的 OP 与 ZK 之争问题 短期 OP&#xff0c;长期 ZK&#xff1f; 这句话因为 V 神说过&#xff0c;所以被很多人认为是「金科玉律」&#xff0c;然而实际情况要复杂的多。 常见的那类比较网上已经说烂 - 即 OP 基于博弈 < ZK 基于数学 &#xff1b;O…

[CISCN2019 总决赛 Day2 Web1]Easyweb

扫目录能扫到一个robots.txt 在页面源代码发现 访问image.php.bak拿到源码 <&#xfeff;?php include "config.php";$idisset($_GET["id"])?$_GET["id"]:"1"; $pathisset($_GET["path"])?$_GET["path"]:&…

Plant Communications|高质量的基因组组装和泛基因组研究促进了绿豆的基因发现及其改进

TITLE&#xff1a;High-quality genome assembly and pan-genome studies facilitate genetic discovery in mungbean and its improvement 译名&#xff1a;高质量的基因组组装和泛基因组研究促进了绿豆的基因发现及其改进 期刊&#xff1a;Plant Communications 日期&#xf…

单卡完美复现pan++论文代码结果

源代码 1、根据源代码github地址下载代码&#xff0c;上传到服务器&#xff0c;使用服务器为3090显卡&#xff0c;cuda11.4 2、所需要的包的版本如下&#xff1a; Package Version -------------------------------- ------------------- absl-py …

【附源码】计算机毕业设计JAVA学生信息管理系统2021

【附源码】计算机毕业设计JAVA学生信息管理系统2021 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA…

solvePnP的使用及物理意义

1 PnP问题概述 PnP问题&#xff1a;Perspective-n-Point问题。 参考下图&#xff0c; 给定n个3D空间参考点&#xff0c;以及各点在相机图像上对应的成像点&#xff0c;求参考点所在坐标系与相机的空间关系。 即&#xff1a; 已知条件1&#xff1a;给定匹配点对&#xff1a;世…

LeetCode | 391.完美矩形

给你一个数组 rectangles &#xff0c;其中 rectangles[i] [xi, yi, ai, bi] 表示一个坐标轴平行的矩形。这个矩形的左下顶点是 (xi, yi) &#xff0c;右上顶点是 (ai, bi) 。 如果所有矩形一起精确覆盖了某个矩形区域&#xff0c;则返回 true &#xff1b;否则&#xff0c;返…

基于Matlab的合成孔径雷达模拟陆地场景(附源码)

目录 一、生成模拟地形 二、指定搜救系统和场景 三、定义地表反射率 四、配置雷达收发器 4.1 生成数据多维数据集 4.2 可视化 SAR 数据 五、总结 六、程序 合成孔径雷达&#xff08;SAR&#xff09;系统使用平台运动来模拟更长的孔径&#xff0c;以提高跨距离分辨率。S…

【半监督图像分割】2021-CPS CVPR

文章目录【半监督图像分割】2021-CPS CVPR1. 简介1.1 简介1.2 相关工作1.3 创新2. 网络2.1 网络架构2.2 Loss2.3 实验3. 代码【半监督图像分割】2021-CPS CVPR 论文题目&#xff1a;Semi-Supervised Semantic Segmentation with Cross Pseudo Supervision 中文题目&#xff1a;…

【第四部分 | JavaScript 基础】3:函数、作用域与预解析、对象

目录 | 函数 声明与调用 参数 返回值 arguments&#xff08;JS特有知识点&#xff09; 命名函数 和 匿名函数 | 作用域 全局和局部、JS5没有块级作用域 就近原则&#xff1a;作用域链 | 预解析&#xff08;重要&#xff09; 导论&#xff1a;四种语句位置导致的现象 …

安装TPDSS

TPDSS使用前电脑必须安装jdk。如若jdk安装完毕&#xff0c;则忽略第一步 第一步&#xff1a;jdk jdk安装完毕后控制面板会出现java字样 jdk安装完毕后&#xff0c;配置环境变量进行生效 1、进入到刚才安装时选择的文件夹&#xff0c;选中上方文件的路径&#xff0c;鼠标右键&…

全国各省产业结构协调-高级化、合理化指标(2000-2020年)

全国各省产业结构协调-高级化、合理化指标&#xff08;2000-2020年&#xff09; 1、时间&#xff1a;2000-2020年 2、包括&#xff1a;30个省份不含西藏 3、来源&#xff1a;统计NJ和国家统计J 4、指标包括&#xff1a; 原始数据&#xff1a;地区生产总值(亿元)、就业人员…

【软件测试】资深测试是如何火速入坑的?测试任务艰巨无从下手?

目录&#xff1a;导读一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;如何快速融入项目团队? 熟悉…

leetcode 1926. Nearest Exit from Entrance in Maze(迷宫最近的出口)

Input: maze [[“”,“”,“.”,“”],[“.”,“.”,“.”,“”],[“”,“”,“”,“.”]], entrance [1,2] Output: 1 Explanation: There are 3 exits in this maze at [1,0], [0,2], and [2,3]. Initially, you are at the entrance cell [1,2]. You can reach [1,0] by …

大模型产业化有四个关键,昇腾AI推动“AI+遥感”打了个样

文|智能相对论 作者|夜远风 农业卫星在太空“拍下”地面这张“照片”&#xff0c;地面根据这些图像数据&#xff0c;结合气象情况等&#xff0c;通过AI算法就准确地“算”出了农作物的长势状况&#xff0c;给地方政府、种地农民以参考。 &#xff08;图&#xff1a;农业用地的…

uni-app入门:页面布局之window和tabbar

1.window 2.tabbar 3.全局配置与局部页面配置 前言 每个页面按照结构可以分成三部分:window page tabbar.其中window和tabbar一般比较固定&#xff0c;page是平常业务开展的主要载体&#xff0c;根据业务需求进行页面配置。下面主要讲一下window和tabbar。 1.window…

【苹果相册】苹果推从新建的私钥CSR文件Profile还分为开发和分发

推荐内容IMESSGAE相关 作者推荐内容iMessage苹果推软件 *** 点击即可查看作者要求内容信息作者推荐内容1.家庭推内容 *** 点击即可查看作者要求内容信息作者推荐内容2.相册推 *** 点击即可查看作者要求内容信息作者推荐内容3.日历推 *** 点击即可查看作者要求内容信息作者推荐…

A-Level经济例题解析及练习

今日知识点&#xff1a; Externality and Government Intervention 例题 Externality and Government Intervention Acme and US Electric run coal-burning power plants. Each emits 40 tons of sulphur dioxide per month, total emissions 80 tons/month. Goal: Redu…