前端如何实现超炫酷的直播礼物动画

3,373 阅读6分钟

前言

在视频直播盛行的时代,每个平台里面都会有直播打赏的功能,消费后会出现一些非常酷炫的动画,今天我们就来看一下那些炫酷的动画如何实现的。

实现思路

首先,从实现思路方面入手,有以下几个方案作为考虑:

    • 方案一:帧动画;
    • 方案二:属性动画;
    • 方案三:Gif或WebP;
    • 动画库
      · Lottie
      · SVGA
      ...

以上三个方案都有自己的不足:

  • 方案一需要把图片带到安装包中,增大了安装包体积,而且直播应用中礼物种类很多,每个礼物都带一套帧动画进去,相当不科学。
  • 方案二开发代价较大,每一套礼物动画基本都不一样,开发人员需要去细细调参,花在业务上编码的时间会变得非常有限,从代码复用率极低。

方案一、二除了以上提到的缺陷外,还有一个直播业务场景问题没有解决,就是当客户端发布之后,想要上架一些新设计的礼物就必须要让用户升级客户端,否则是无法体验到新上架的礼物。

方案三没有以上提到的业务问题,但在性能方面并不是最优选择。Gif图实质上就是把一帧帧的静态图片打包到一起,打成一个压缩包,但是由于Gif的压缩算法问题,实际上这个压缩包一点都不小,随便一个炫酷点的礼物效果都能弄个几兆到十几兆不等的文件出来,对于移动端来说,不论从下载和播放的性能表现上都差强人意,总结下来就是1、动画文件太大

2、播放资源占用高

3、效果差(GIF只支持8位颜色)。Gif图片能做的动画,WebP格式的图片同样可以实现,且WebP图片要比Gif图小很多,下图是同样效果用Gif和WebP的实现对比。

那么,使用WebP呢?嗯,使用WebP可以把动画文件大小压缩到一半,但是,播放资源占用问题没有解决,这依然是头疼的问题,想想,一个视频直播流已经占用了系统给进程分配的大量内存,然后直播间里面可能成千上万甚至更大体量的人同时在线,观众们一个个赠送礼物的动画出来,应用直接给搞挂掉了……

动画库比较

支持平台Android/iOS/WebAndroid/iOS/WebAndroid/iOSiOSAndroid/iOS/Web
设计工具支持After EffectsAE & FlashAEAEAE
功能边界所有部分矢量图矢量图大部分
导出工具插件插件脚本插件单独的设计工具
设计成本需要命名规范需要脚本插件单独的设计工具
资源包大小zip2.6M767k2M
收费NNNYY
动画库LottieSVGAKeyframesSquallSpine

SVGA 和 Lottie 比较

动画库优点缺点
SVGA- 资源包小
  • 测试工具齐全- 三端可用
  • 回调完整- Protobuf 序列化结构数据格式,序列化的数据体更小,传递效率比xml,json 更高。
  • 支持,需定义一套专属的头像配置的协议。 | - 每个礼物播放时都去新解压,需要改一套缓存策略
  • svga 用zlib打包(字节流数据压缩程序库),不方便解压和追踪包内容。 | | Lottie | - 三端可用
  • 回调完整- 项目已经存在一套缓存逻辑
  • 当前的库可以满足业务需求,不需要二次开发 | - 资源包相较SVGA而言会大一倍多
  • 图片需要重命名 & 偶显播不出来动效。 |

性能对比

  • 同一礼物对比
内存占用350339
graphics(显卡)116-117113 -103
cpu 占用平均5.6,最高15平均5.18,最高13.75
项目SVGALottie
内存占用390362
graphics(显卡)144-138114-114
cpu 占用平均6.8,最高17平均7.4,最高23
项目SVGALottie

svga定义

简洁的官方定义:SVGA 是一种同时兼容 iOS / Android / Flutter / Web 多个平台的动画格式。
svga是全新的动画格式,提供高性能动画播放体验。

实现思路:

    • 一帧一帧
    • 通过设置帧率,来生成一个配置文件,使得每一帧都有一个配置,每一帧都是关键帧,通过帧率去刷每一帧的画面,这个思路跟gif很像,但是通过配置使得动画过程中图片都可以得到复用。性能就提升上来了。并且不用解析高阶插值(二次线性方程,贝塞尔曲线方程)

兼容性

SVGAPlayer 2.0.0 只支持以下浏览器使用

  • Edge / IE 6+
  • Safari / Chrome
  • iOS 6.0+ / Android 4.0+

安装

预编译js

添加 到 your.html 页面

NPM

    npm install svgaplayerweb --save
    1.  添加 const SVGA = require('svgaplayerweb'); 或 import SVGA from 'svgaplayerweb'; 至 xxx.js
    

使用步骤

流程图:
1、创建canvas容器

<div id="demoCanvas" style="styles..."></div>

2、加载动画

var player = new SVGA.Player('#demoCanvas');
var parser = new SVGA.Parser('#demoCanvas');
parser.load('rose_2.0.0.svga', function(videoItem) {
    player.setVideoItem(videoItem);
    player.startAnimation();
})

API:

Properties

  • int loops; - 动画循环次数,默认值为 0,表示无限循环。
  • BOOL clearsAfterStop; - 默认值为 true,表示当动画结束时,清空画布。
  • string fillMode; - 默认值为 Forward,可选值 Forward / Backward,当 clearsAfterStop 为 false 时,Forward 表示动画会在结束后停留在最后一帧,Backward 则会在动画结束后停留在第一帧。

Methods

  • constructor (canvas); - 传入 #id 或者 CanvasHTMLElement 至第一个参数
  • startAnimation(reverse: boolean = false); - 从第 0 帧开始播放动画
  • startAnimationWithRange(range: {location: number, length: number}, reverse: boolean = false); - 播放 [location, location+length] 指定区间帧动画
  • pauseAnimation(); - 暂停在当前帧
  • stopAnimation(); - 停止播放动画,如果 clearsAfterStop === true,将会清空画布
  • setContentMode(mode: "Fill" | "AspectFill" | "AspectFit"); - 设置动画的拉伸模式
  • setClipsToBounds(clipsToBounds: boolean); - 如果超出盒子边界,将会进行裁剪
  • clear(); - 强制清空画布
  • stepToFrame(frame: int, andPlay: Boolean); - 跳到指定帧,如果 andPlay === true,则在指定帧开始播放动画
  • stepToPercentage(percentage: float, andPlay: Boolean); - 跳到指定百分比,如果 andPlay === true,则在指定百分比开始播放动画
  • setImage(image: string, forKey: string, transform: [a, b, c, d, tx, ty]); - 设定动态图像, transform 是可选的, transform 用于变换替换图片
  • setText(text: string | {text: string, family: string, size: string, color: string, offset: {x: float, y: float}}, forKey: string); - 设定动态文本
  • clearDynamicObjects(); - 清空所有动态图像和文本

Callback Method

  • onFinished(callback: () => void): void; - 动画停止播放时回调
  • onFrame(callback: (frame: number): void): void; - 动画播放至某帧后回调
  • onPercentage(callback: (percentage: number): void): void; - 动画播放至某进度后回调