如何优雅的实现动画效果

如何,优雅,实现,动画,效果 · 浏览次数 : 43

小编点评

**1. 实现动画的方式JavaScript** ```javascript // setTimeout setTimeout(function() { console.log('🚀🚀hello ~ setTimeout'); }, 1000); // setInterval setInterval(function() { console.log('🚀🚀hello ~ setInterval'); }, 1000); // animationhtml: canvasrequestAnimationFrame Api const canvas = document.getElementById('box'); const ctx = canvas.getContext('2d'); const animate = function() { // 每次重绘的回调函数 requestAnimationFrame(animate); // 在下次重绘之前更新动画 ctx.fillStyle = 'blue'; ctx.fillRect(0, 0, 50, 50); }; // 开始动画 animate(); ``` **2. 定义动画时间回调函数** ```javascript // requestAnimationFrame2.1 定义动画 requestAnimationFrame2.defineFrame(function(timestamp) { console.log(`🚀🚀hello ~ requestAnimationFrame ${timestamp}`); }); ``` **3. 频率回调函数** ```javascript //回调函数参数包含时间戳 requestAnimationFrame(function(test) { // 在下次重绘之前更新动画 console.log(`🚀🚀hello ~ requestAnimationFrame ${timestamp}`); }); ``` **4. 动画例子** ```html

requestAnimationFrame API

``` **5. 比较方法** | 方法 | setTimeout | setInterval | requestAnimationFrame | |---|---|---|---| | 频率 | 不精确 | 精准 | 频率 | | 内置时间间隔 | 不使用 | 使用 Date.now() | 浏览器时间间隔 | | 用途 | 频繁执行函数 | 精准计时 | animation |

正文

1.实现动画的方式

  • javaScript:setTimeout、setInterval
  • css3: transition、animation
  • html: canvas
  • requestAnimationFrame Api
    前三种我们都很熟悉,重点说说requestAnimationFrame

2.requestAnimationFrame

2.1 定义

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

  //html代码全文通用,所以只在此贴出一次
<body>
  <h1>requestAnimationFrame API</h1>
  <button id='begin' class="begin">开始</button>
  <button id='end' class="end">停止</button>
</body>

//js
(() => {
  function test() {
    console.log('🚀🚀hello ~ requestAnimationFrame');
  }
  requestAnimationFrame(test)
})()

上述代码只执行了一下,为什么会这样呢?看看mdn怎么说。
注意:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()
所以我们可以这么写:

  (() => {
  let n = 0
  function test() {
    n++
    console.log(`🚀🚀hello ~ requestAnimationFrame ${n}`);
    requestAnimationFrame(test)
  }
  requestAnimationFrame(test)
})()

2.2 执行频率

回调函数执行频率通常60次/秒,与浏览器屏幕刷新次数相匹配

2.3 回调参数

回调函数会被传入DOMHighResTimeStamp参数,DOMHighResTimeStamp指示当前被 requestAnimationFrame() 排序的回调函数被触发的时间。

(() => {
  function test(timestamp) {
    console.log(`🚀🚀hello ~ requestAnimationFrame ${timestamp}`);
    requestAnimationFrame(test)
  }
  requestAnimationFrame(test)
})()

timestamp释义:在同一个帧中的 多个回调函数 ,它们每一个都会接受到一个 相同的时间戳 ,即使在计算上一个回调函数的工作负载期间已经 消耗了一些时间 。该时间戳是一个十进制数,单位毫秒,最小精度为1ms(1000μs)。

性能问题

3.1 浏览器自身措施:

为了提高性能和电池寿命,因此在大多数浏览器里,当requestAnimationFrame() 运行在后台标签页或者隐藏的iframe 里时,requestAnimationFrame() 会被暂停调用以提升性能和电池寿命。

3.2开发人员限制

3.2.1 requestAnimationFrame会返回一个非0整数,通过cancelAnimationFrame 可停止动画

(() => {
  const beginBtn = document.querySelector("#begin")

  const endBtn = document.querySelector("#end")

  let myRef;

  beginBtn.addEventListener("click", () => {
    myRef = requestAnimationFrame(test)
  })

  endBtn.addEventListener("click", () => {
    cancelAnimationFrame(myRef)
  })

  function test() {
    myRef = requestAnimationFrame(test)
    console.log('🚀🚀~ myRef:', myRef);
  }
})()

3.2.2 在代码中做一些操作

(() => {
  function test(timestamp) {
    console.log(`🚀🚀hello ~ requestAnimationFrame ${timestamp}`);
    if (timestamp < 500) {
      requestAnimationFrame(test)
    }
  }
  requestAnimationFrame(test)
})()

4.动画例子

<style>
  #box {
    width: 0px;
    height: 50px;
    background-color: blue;
  }
</style>
<body>
  <h1>requestAnimationFrame API</h1>
  <button id='begin' class="begin">开始</button>
  <button id='end' class="end">停止</button>
  <div id='box'></div>
</body>
(() => {
  const beginBtn = document.querySelector("#begin")
  const endBtn = document.querySelector("#end")
  const box = document.querySelector("#box")
  let myRef;

  beginBtn.addEventListener("click", () => {
    myRef = requestAnimationFrame(test)
  })

  endBtn.addEventListener("click", () => {
    cancelAnimationFrame(myRef)
  })

  function test() {
    box.style.width = `${myRef}%`
    myRef = requestAnimationFrame(test)
  }
})()

5.对比

5.1 setTimeout && setInterval

setTimeout 和 setInterval 的问题是,它们不够精确。它们的内在运行机制决定了 时间间隔参数 实际上只是指定了把动画代码添加到 浏览器UI线程队列 中以等待执行的时间。如果队列前面已经加入了其它任务,那动画代码就要等前面的 任务完成后 再执行,并且如果时间间隔过短(小于16.7ms)会造成丢帧,所以就会导致动画可能不会按照预设的去执行,降低用户体验。
requestAnimationFrame 采用 浏览器时间间隔 ,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,消耗性能;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个 统一 的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。

5.2 CSS3动画

CSS3 的transition 和 animation 搭配使用可以说是非常强大了,但是也有的触手伸不到的地方,比如说
scrollTop,另外 CSS3 动画支持的贝塞尔曲线也是有限的。
那么,CSS3 做不到的就可以用到 requestAnimationFrame 来解决了。

tips 利用该方法可以代替setTimeout

例如 每间一秒执行一次函数

    (() => {
    let startTime = Date.now();

    function handleTicker() {
      foo(Date.now() - startTime);
      startTime = Date.now();
      requestAnimationFrame(handleTicker);
    }

    requestAnimationFrame(handleTicker);

    let t = 0
    function foo(timeInterval) {
      t += timeInterval
      console.log('🚀🚀~ t:', t);
      if (t > 1000) {
        console.log('🚀🚀~ 搞事情');
        t = 0
      }
    }
  })()

参考:

与如何优雅的实现动画效果相似的内容:

如何优雅的实现动画效果

1.实现动画的方式 javaScript:setTimeout、setInterval css3: transition、animation html: canvas requestAnimationFrame Api 前三种我们都很熟悉,重点说说requestAnimationFrame 2.re

Vue Router 4与路由管理实战

这篇文章介绍了如何在Vue.js应用中利用Vue Router实现单页面应用的路由管理,包括配置路由、导航守卫的使用、路由懒加载以优化性能以及动态路由的实现方法,旨在提升用户体验和应用加载效率

efcore如何优雅的实现按年分库按月分表

efcore如何优雅的实现按年分库按月分表 介绍 本文ShardinfCore版本 本期主角: ShardingCore 一款ef-core下高性能、轻量级针对分表分库读写分离的解决方案,具有零依赖、零学习成本、零业务代码入侵适配 距离上次发文.net相关的已经有很久了,期间一直在从事java相关的

面向状态机编程:复杂业务逻辑应对之道

在研发项目中,经常能遇到复杂的状态流转类的业务场景,比如游戏编程中NPC的跳跃、前进、转向等状态变化,电商领域订单的状态变化等。这类情况其实可以有一种优雅的实现方法:状态机。本文重点介绍有限状态机,并结合具体项目,通过状态机的应用将状态和业务逻辑解耦,便于简化复杂业务逻辑,降低理解成本。另外,重点讲解如何优雅的解决更广泛的复杂业务问题。

[转帖]kill及kill -9的用法及如何实现进程的优雅退出

1. kill与signals 我们这里所说的kill是指作为shell command的那个kill(相对地,linux 系统中还有个叫做kill的system call, man 2 kill可查看其功能及用法),shell终端中输入man kill可以看到,kill的作用是向某个指定的进程或进

[转帖]k8s发布Spring cloud+eureka架构服务优雅启动停止方案

本文转载自昆仑枫的简书https://www.jianshu.com/p/6d393cbb694a Spring cloud+eureka是目前微服务主流解决方案之一,kubernetes则是广泛应用的发布工具,两者结合使用很常见。而两者结合时如何优雅启停从而实现无感发布很关键。下面将从不做特殊处理

Libgdx游戏开发(7)——开始游戏界面实现

原文: Libgdx游戏开发(7)——开始游戏界面实现-Stars-One的杂货小窝 上篇文章也是讲解了如何实现暂停,但实际上,上篇的做法可能不够优雅 因为暂停和游戏界面我们可以分成2个Screen对象,这样只需要监听键盘输入,更改显示不同的Screen对象即可 本文的实现目标: 使用Screen来

OpenTelemetry 实践指南:历史、架构与基本概念

背景 之前陆续写过一些和 OpenTelemetry 相关的文章: 实战:如何优雅的从 Skywalking 切换到 OpenTelemetry 实战:如何编写一个 OpenTelemetry Extensions 从一个 JDK21+OpenTelemetry 不兼容的问题讲起 这些内容的前提是最

使用 Promise.withResolvers() 来简化你将函数 Promise 化的实现~~

引言 在JavaScript编程中,Promise 是一种处理异步操作的常用机制。Promise 对象代表了一个尚未完成但预期将来会完成的操作的结果。在本文中,我们将探讨如何通过使用 ES2024 的 Promise.withResolvers API 来优化我们的 Promise 实现。 现有实现

如何实现一个优秀的 HashTable 散列表?

本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 前言 大家好,我是小彭。 在前几篇文章里,我们聊到了 Java 中的几种线性表结构,包括 ArrayList、LinkedList、ArrayDeque 等。今天,我们来讨论另一种常用的基础数据结构,同时也是