动画的基本原理
什么是动画
动画是通过快速连续排列彼此差异极小的连续图像来制造运动错觉和变化错觉的过程。 ———维基百科
- 常见的前端动画技术:Sprite 动画、CSS 动画、JS 动画、SVG 动画和 WebGL 动画
- 动画按应用分类:UI 动画、基于 WebGL的游戏动画和动画数据可视化
计算机动画原理
计算机图形学:计算机视觉的基础,涵盖点、线、面、体、场的数学构造方法
- 几何和图形数据的输入、存储和压缩
- 描述纹理、曲线、光影等算法
- 物体图形的数据输出(图形接口、动画技术),硬件和图形的交互技术
- 图形开发软件的相关技术标准
- 计算机动画:计算机图形学的分支,主要包含 2D、3D 动画
- 无论动画多么简单,始终需要定义两个基本状态即开始状态和结束状态,有了这两个状态才能定义插值状态从而填补两者之间的空白
- 帧:连续变换的多张画面,其中的每一幅画面都是一帧
- 帧率:用于度量一定时间段内的帧数,通常的测量单位是 FPS (frame per second)
- 帧率与人眼:一般每秒 10 ~ 12 帧人会认为画面是连贯的,这个现象称作视觉暂留。对于一些动画来说低于 30FPS 会感觉到明显卡顿,目前主流的屏幕、显示器输出为 60FPS,效果明显流畅
- 空白的补全方式有两种:补间动画(关键帧)和逐帧动画
前端动画分类
CSS 动画
CSS animation 是常见的 CSS 动画实现方式
- animation 是 animation-name、animation-duration、animation-timing-function、animation-delay、animation-iteration-count、animation-direction、animation-fill-mode、animation-play-state 属性的一个简写属性形式
- animation-name 属性指定应用的一系列动画,每个名称代表一个由 @keyframes 定义的动画序列
- animation-duration 属性指定一个动画周期的时长
CSS 实现补间动画(Transition API、Keyframe)
- Transition API(过渡动画):DOM 加载完成或 class 发生变化时触发
- keyframe 实现动画:和 transition 相比,关键帧 keyframes 可以控制动画序列的中间步骤
- sprite 实现 CSS 逐帧动画
CSS 动画特点:
- 优点:简单、高效、声明式的、不依赖于主线程,采用硬件加速(GPU);浏览器会对 CSS3 动画做优化,性能上有优势
- 缺点:不能动态修改或定义动画内容,不同的动画无法实现同步,多个动画彼此无法堆叠,部分动画无法实现
- 适用场景:简单的 H5 活动/宣传页面
- 推荐库:animation.css、shake.css等
SVG 动画
- SVG 是基于 XML 的矢量图形描述语言,它可以和 CSS 和 JavaScript 较好地配合,实现 SVG 动画通常有三种方式:SMIL(Synchronized Multimedia Integration Language 同步多媒体集成语言)、JavaScript、CSS
- 使用 JavaScript 实现 SVG 动画的类库有 snap.js、anime.js 和 HTML 原生的 Web Animation
SVG 动画特点:
- 优点:通过矢量元素实现动画,不同屏幕下均可获得较好的清晰度,可以实现一些特殊的效果:描字、形变和墨水扩散等
- 缺点:使用方式较为复杂,过多使用可能会带来性能问题
JavaScript 动画
- JavaScript 可以实现复杂的动画,也可以操作 canvas 动画 API 上进行绘制
JavaScript 动画特点:
- 优点:使用灵活,相较 CSS 容易做到两个以上的状态转化
- 缺点:使用到 JS 运行时,调优方面不如 CSS 简单,对于性能和兼容性较差的浏览器 CSS 可以做到优雅降级,而 JS 需要额外代码兼容
如何选择
- 为 UI 元素采用较小的独立状态时使用 CSS
- 在需要对动画进行大量控制时,使用 JavaScript
- 在特定场景下可以使用 SVG,可以使用 CSS 或 JS 操作 SVG 变化
实现前端动画
JavaScript 动画封装函数
- JavaScript 动画应该通过 requestAnimationFrame 内置函数,允许设置回调函数以在浏览器准备重绘时允行,当页面在后台时没有发生重绘,回调不会运行,动画将被暂停且不会消耗资源
- JavaScript 实现动画不建议使用 setTimeout 和 setInterval
- JavaScript 执行动画的核心思想:$Δr = ΔvΔt$ ,r 是距离,v 是速度,t 是时间
JavaScript 动画实现代码:
/** * 参数说明: draw 绘制函数 easing 缓动函数 duration 持续时间 @returns 返回一个 Promise 对象,是因为动画可以是连续的,支持通过then函数或await进行顺序调用 */ function animate({ easing, draw, duration }) { // 动画开始的时间戳 // 不使用 new Date.now() 因为performance.now()会以恒定速度自增,精确到微秒级别,不易被篡改 let start = performance.now(); return new Promise(resolve => { requestAnimationFrame(function animate(time) { let timeFraction = (time - start) / duration; if (timeFraction > 1) timeFraction = 1; let progress = easing(timeFraction); draw(progress); if (timeFraction < 1) { requestAnimationFrame(animate); } else { resolve(); } }); }); }
draw 绘制函数
const ball = document.querySelector('.ball'); const draw = (progress) => { ball.style.transform = `translate(${progress}px, 0)`; }
easing 缓动函数
easing(timeFraction) { return timeFraction ** 2; }
相关实践
动画资源
- SVG:Snap.svg - 现代SVG图形的JavaScript库、Svg.js - 用于操作和动画SVG的轻量级库
- JavaScript:GSAP - JavaScript动画库、TweenJs - 一个简单但功能强大的JavaScript补间/动画库。CreateJS库套件的一部分、Velocity - 加速的JavaScript动画
- CSS:Animate.css - CSS动画的跨浏览器库。像一件简单的事情一样容易使用
- canvas:EaselJs - 是一个用于在HTML5中构建高性能交互式2D内容的库、Fabric.js - 支持动画的JavaScript画布库、Pixijs - 使用最快、最灵活的2D WebGL渲染器创建精美的数字内容
动画的优化
- 性能角度:页面渲染的一般过程为 JS > CSS > 计算样式 > 布局 > 绘制 > 渲染层合并,即 JavaScript > Style > Layout > Paint > Composite
- 其中,Layout(重排)和Paint(重绘)是整个环节中最为耗时的两环,所以我们尽量避免这两个环节,从性能方面考虑,最理想的渲染流水线是没有布局和绘制环节的,只需要做渲染层的合并即可(重排必然重绘,重绘不一定重排)。在实际的应用里,最为简单的一个注意点就是,触发动画的开始不要用 display: none 属性值,因为它会引起 Layout、Paint 环节,通过切换类名就已经是一种很好的办法
另外一些优化方案:
- translate 属性值来替换 top/left/right/bottom 的切换
- scale 属性值替换 width/height
- opacity 属性替换 display/visibility
- CSS3 硬加速(GPU 加速):使用 transform、opacity、filter、will-change 属性引起硬加速
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。