首先梳理web场景下,动画播放的几种方式。
方案对比
目前较常见的动画实现方案有原生动画、帧动画、gif/webp、lottie/SVGA,对于复杂动画特效的实现做个简单对比
方案 | 优势 | 劣势 |
---|---|---|
css动画 | 使用方便 | 还原程度 取决工程师和 动画复杂程度; 复杂动画实现成本高 |
序列帧动画 | 实现成本低 | 还原程度中,比较固定,部分复杂特效不支持,且资源消耗大 |
gif | 实现成本低 | 还原度低,只支持8位(256)颜色,且资源消耗大,质量较大。最重要的 不能交互 |
webp | 实现成本低 | 还原程度中,资源消耗大;考虑IOS兼容问题,最重要的 不能交互 |
Lottie/SVGA | 实现成本低 | 部分复杂特效不支持 |
透明mp4视频 | 性能好,一次接入永久使用 | 还原度高 |
css动画
在实现简单页面交互上是比较常用,当设计师提供较高要求的 动画效果,就比较挠头了。
gif
实现简单,质量较大。最重要的 不能交互。
Webp
Webp相比PNG和JPEG格式体积可以减少25%,在移动端的平台支持上也很全面,支持24位RGB色;缺点是资源体积比较大,而且使用的软解效率低下,低端机上有明显卡顿问题。且在比较低版本的ios 机型存在 比较大的兼容问题。 最重要的 不能交互。
在不涉及mask和mattes等特性时性能非常优秀,主要耗时基本集中在Canvas#draw()上而已。然而在设计实现复杂遮罩、光影渐变上效果一般且异常消耗性能。对于直播场景的复杂特效动画而言,他们就不是一个很合适的实现方案了。
透明mp4视频 方案
透明视频 由于出色的还原度和良好的性能,非常适合 复杂直播场景。 又详细分为以下几类:
方案 | 优势 | 劣势 |
---|---|---|
video + css 滤色(cssmix-blend-mode: screen; ) | 工作量小、体积小 | 只适用于黑色背景视频 |
webm透明视频 | 原生支持 | 兼容性差(透明只在Chrome和Opera中得到支持) |
canvas 绘制透明视频 | 动画效果好 | CPU占用高 |
webgl绘制透明视频 | 动画效果好,CPU占用小 | 学习成本 |
video + css 滤色 实现透明
实现过程:
一个黑色背景的 **特效图片** 或者 **视频** 覆盖在目标 层上,加上 `mix-blend-mode: screen;` css 样式,即可实现 透明 图片或视频 播放动画的效果。
其实现逻辑基于 混色计算方式:
C = 255 - (255-A) ✖️ (255-B) / 255
如: A 是 红色 RGB(255,0,0); B 是 蓝色 RGB(0,0,255)
那么:
R = 255 - (255 - 255) * (255 - 0) / 255 = 255
G = 255 - (255 - 0) * (255 - 0) / 255 = 0
B = 255 - (255 - 0) * (255 - 255) / 255 = 255
直观特性:
- 任何颜色和黑色执行滤色,还是呈现原来的颜色;
- 任何颜色和白色执行滤色得到的是白色;
- 任何颜色和其他颜色执行滤色模式混合后的颜色会更浅,有点类似漂白的效果。
详细讲解 戳 深入理解CSS mix-blend-mode滤色screen混合模式
其实这种 过于简单粗暴了,如果我们的动画 背景是 黑色透明度的呢? 上面的计算可是 没有 Alpha 通道的。这时候就要说到 绘制透明视频了
webgl绘制透明视频
原理
首先看段 视频, 这是我们设计师 实际导出 的动画视频(别误会,右侧 不是出了问题)。


我们会发现视频播放 右侧 类似高度曝光的视频,其实 记录的 Alpha 通道 的值,通常被放在 R通道。
所以 我们取出 右侧视频区域的R通道的值,与左侧原视频的RGB值 进行重新组合,绘制 RGBA 的 图片即可。然后 一帧一帧的播放 绘制即可。即可实现 上面的 第二张图的 透明视频的动画效果。
基本原理
如果还有不明白 可以看下 这里的解释
主要有两个部分,一个是视频播放器,负责视频解码;另一个是绘制器,负责将解析出来的每一帧画面进行透明度 混合,再绘制到 Canvas 画布上。
- 视频播放器 : 在素材是 MP4 的情况下,其实使用 浏览器的 video 就完全可以胜任了【 毕竟使用其他播放器解码又要增加代码体积了 】
- 绘制器: 至于 到底使用canvs 还是 webGL 其实就是看性能了。毕竟 webGL直接使用 GPU硬件,性能上要高上不少。
实现
视频播放器:
1 |
|
绘制器
在使用 webGL 绘制前 ,我们需要了解一些概念。
WebGL 主要是 低级光栅化 API, 不是 3D API。要使用 WebGL 绘制图像,您必须传递表示 图像的向量。然后,它使用 OpenGL SL 将给定的 矢量 转换为 像素格式,并在屏幕上显示图像。
着色器
着色器 是使用 OpenGL ES 着色语言(GLSL) 编写的程序,它携带着 记录着像素点的位置和颜色 的信息。
GLSL编写 着色器程序,并将代码文本传递给 WebGL,在 GPU 执行时编译
着色器 分为: 顶点着色器和片段着色器顶点着色器 :
主要记录着像素点的位置, 决定了 3D 物体在屏幕上的位置和形状
- 每次渲染一个形状时,顶点着色器 会在形状中的 每个顶点运行。
它的工作是将 输入顶点 从原始坐标系转换到 WebGL 使用的 裁剪空间 坐标系,其中每个轴的坐标范围从 -1.0 到 1.0,并且不考虑纵横比,实际尺寸或任何其他因素;
顶点着色器根据需要,也可以完成其他工作。例如,决定哪个包含 texel 面部纹理的坐标,可以应用于顶点;通过 法线 来确定 应用到顶点的光照因子等。 然后将这些信息存储在 变量(varyings)或属性 (attributes)属性中,以便与片段着色器共享。
- 每次渲染一个形状时,顶点着色器 会在形状中的 每个顶点运行。
片段着色器:
主要记录着像素点的颜色
- 片段着色器在 顶点着色器 处理完图形 的顶点后,会被要绘制的每个图形的每个像素点调用一次。
它的职责是 确定像素的颜色,通过指定应用到像素的纹理元素(也就是图形纹理中的像素),获取纹理元素的颜色,然后将 适当的光照应用于颜色。之后颜色存储在特殊变量 gl_FragColor 中,返回到 WebGL 层。该颜色将最终绘制到屏幕上图形对应像素的对应位置。
- 片段着色器在 顶点着色器 处理完图形 的顶点后,会被要绘制的每个图形的每个像素点调用一次。
基本过程
1 | 创建着色器(顶点、片段)程序 ====> 绘制 |
那么我们的思路就是 在着色器上 使用 左侧的视频帧 为底 + 右侧视频帧 的R通道最为 A通道, 那么
1 |
|
如果 ,我们的资源 反过来呢? 右侧的视频帧 为底 + 左侧视频帧 的R通道最为 A通道。 如何处理?对坐标转换下。
1 | .... |
当然别忘了, canvas 在 高清屏幕 绘制模糊的问题,canvas-更正分辨率
当然要修正下。
好上完整代码:
1 |
|
调用测试下:
1 |
|