Web Animation 网页动画

Last updated: 2023-05-06
IntroductionSlides

Web Animation 网页动画

  1. 什么是动画?
  2. 网页动画简史
  3. 网页动画技术
  4. 网页动画的工作原理
  5. Framer Motion 简介

1. 什么是动画?

动画

通过定时拍摄多个静止的固态图像(帧),以一定频率连续变化而导致肉眼的视觉残象产生错觉,而误以为图像或物体活动的技术。

视觉暂留

光对视网膜所产生的视觉,在光停止作用后,仍然保留一段时间的现象。原因是由视神经的反应速度造成的,其时值约是 1/16 秒,对于不同频率的光有不同的暂留时间。是现代影视、动画等视觉媒体制作和传播的根据。

从史前文明到如今的信息时代,在人类文化中几乎都贯穿着动画的身影。现在,我们特别关注关于前端的动画:

Web Animation 网页动画

2. 网页动画简史

First website

有史以来创建的第一个网站的样子,当时甚至缺少 CSS。

GIF Demo

Netscape Navigator 于 1995 年支持了 GIF 在浏览器中的使用。

Flash demo

90 年代后期,Flash 被发明并被逐渐使用,随后十几年 Flash 在网络上成为流行的动画实现方式。

在 2010 年的 4 月,苹果公司的 CEO 乔布斯发表一篇题为“对 Flash 的思考”的文章,指出随着 HTML5 的发展,观看视频或其它内容时,Adobe Flash 将不再是必须的。

iOS 不支持 Flash 的真实原因是什么?

2014 年 10 月 28 日,W3C 正式发布 HTML5 推荐标准。

CSS3 标准自 1999 年开始制定,采用了模块化的规范制定方式。在 2009 年发布了与动画相关的 animations 和 transform 模块公开草案。

  • 移动互联网的快速发展
  • HTML/CSS/JavaScript 规范对新特性的支持逐渐完善
  • Flash 慢性死亡

使用前端技术开发网页动画逐渐成为主要开发方式

3. 网页动画技术

  • CSS
  • JavaScript
  • WebGL(WebGPU)

CSS Animations

  1. transition
.ele {
  transition: opacity 4s ease-in-out;
  opacity: 0.5;
}
.ele:hover {
  opacity: 1;
}
  1. animation
.ele {
  animation: 4s linear 0s infinite alternate move;
}
@keyframes move {
  from {
    margin-left: -20%;
  }
  to {
    margin-left: 100%;
  }
}

.pause {
  animation-play-state: paused;
}
.active {
  animation-play-state: running;
}

贝塞尔曲线

bezier

CSS 中的cubic-bezier() 由 4 个控制点构成。并且默认第 1 个和第 4 个固定坐标为 (0, 0)(1, 1)cubic-bezier(x2, y2, x3, y3) 定义的是第 2、3 个控制点的坐标。

JavaScript Animations

  1. setInterval/setTimeout

    setInterval(function () {
      // change element style
      ele.style.left = left + 5 + 'px'
    }, 16)
    
  2. requestAnimationFrame

  3. Web Animations API (WAAPI)

requestAnimationFrame

requestAnimationFrame(callback: (time: number) => void)

function step() {
  if (ele.style.left < 200) {
    // set element style
    ele.style.left = left + 5 + 'px'
    requestAnimationFrame(step)
  }
}
requestAnimationFrame(step)

Web Animations API

JavaScript 将最新的动画支持称为 Web Animations API (WAAPI)。

Using the Web Animations API

const cake = document.getElementById('#cake')

const animateCake = cake.animate(
  [{ transform: 'translateY(0)' }, { transform: 'translateY(-80%)' }],
  {
    easing: 'steps(4, end)',
    duration: aliceChange.effect.timing.duration / 2,
  },
)
// 开始/暂停
animateCake.pause()
animateCake.play()
// 结束/中断
animateCake.cancel()
animateCake.finish()
animateCake.reverse()
// 调整速率
animateCake.playbackRate *= 1.1
// ...

CSS 与 JavaScript 的性能差异

根据MDN - CSS 动画与 JavaScript 动画的性能的结论

事实上,大多数场景下,基于 CSS 的动画几乎是跟 JavaScript 动画表现一致。一些基于 Javascript 的动画库,甚至声称他们在性能上可以做得比原生 CSS 更好。

这是可能的,因为在重绘事件发生之前,CSS transition 和 animation 在 UI 线程仅仅是重新采集元素的样式,这跟通过 requestAnimationFrame() 回调获取重新采集元素样式是一样的,也是在下一次重绘之前触发。假如二者都是在主 UI 线程创建的动画,那它们在性能方面没有差异。

  • CSS 动画更适合简单无需复杂控制的动画场景
  • JavaScript 更适合需要对动画进行控制,如调整动画速率、调整动画时序等动画场景
  • 第三方动画库借助 JavaScript 可以实现更强大的动画能力

4. 网页动画的工作原理

定义动画始终需要定义两个基本状态,即开始和结束状态。通过计算插值状态,实现动画效果。

Progression = f(Time)

例如

  • 线性: f = (x) => x
  • 速率从 0 开始的 n 次幂加速: f = (x) => Math.pow(x, n)

访问 ts-easing 查看一些常见的插值动画函数

animate

function animate(callback, duration = 2500, f = easing.ease) {
  let start = performance.now()
  const loop = time => {
    // progression 从 0 增加到 1
    let progression = (time - start) / duration
    if (progression > 1) progression = 1
    // 计算当前动画状态
    let progress = f(progression)
    callback(progress) // 绘制
    if (progression < 1) {
      requestAnimationFrame(loop)
    }
  }
  requestAnimationFrame(loop)
}

Framer Motion

  • react-spring
  • framer-motion
  • gsap
  • matter.js
    ...

Framer Motion 基本概念

Framer Motion 是一个 React 动画库,它提供了一系列支持动画的基础组件、事件方法及 hooks 方法供开发者方便快捷地实现动画效果。

<motion.div animate={{ x: 100 }} whileTap={{ y: 10 }} />
  • motion 支持动画效果的组件,支持定义动画帧,支持通过 transition 定义过渡效果。
  • whileTap whileHover 等支持动画效果的方法。
  • variants 支持在多个不同的动画帧状态之间切换。
  • layout 支持布局动画。

Layout Animation

<div className="flex">
  <motion.div layout style={{ justifySelf: position }}></motion.div>
</div>

LayoutId

通过 LayoutId 可以将不同的组件进行关联,从而实现在非同一个页面元素实现动画切换的效果。

Introducting FLIP

  1. First: ele.getBoundingClientRect() 获取初始位置 {x: 0}
  2. Last: 获取终点位置 {x: 10}
  3. Inverse: 此时已经有了起始点的信息,计算终点状态反转到初始状态需要的转换参数 {transform: translateX(-10)}
  4. Play: 将第三步的转换参数归零,并添加动画过渡效果 translateX(-10) -> translateX(0)

总结

  1. 网页动画经历了很多年的发展,最终形成了通过 CSS,JavaScript 编写动画的主流方式。
  2. CSS 通过定义动画帧可以快速的实现简单的动画。
  3. 开发者可以通过 window.requestAnimationFrame 方法使用 JavaScript 开发高性能的动画效果。同时也有原生的 Web Animations API 可供选择使用,但目前仍需要考虑兼容性问题。
  4. React 开发者可以通过优秀的第三方库如 framer motion 简单快速的构建动画效果。

参考