使用了一年多的另一个 SVGA Web 播放器现分享给大家
SVGA
SVGA ( https://svga.io) 是 YY 研发的一套很棒的动画方案,这个方案在公司内大规模使用,因为是一套从设计到研发一套打通的方案,所以效率和整体体验都算不错.
不过官方的 Web 版播放器使用起来有几个痛点:
-
SVGA 最初应该没有考虑太多的流程控制,不过现实业务中经常性的是通过多个 SVGA 组合进行逻辑动画播放,比如 A 播 XX 帧之后再播放 B,然后再播放 C ;或者 A 原地 n 到 m 帧循环,用户点击后跳转到 p 帧播一次;官方的 API 在处理这个情景上不是那么自然,当场景复杂了之后需要写额外的封装处理.
-
官方播放器提供了多种缩放模式,但使用起来有些别扭,而且没有办法使用 CSS 样式控制舞台尺寸,当使用 CSS 设定宽高时舞台内容会出现没有规律的奇怪变形,灵活性稍微差了点,我们平常使用 CSS 的 scale 来处理.
-
某些版本的 IOS 平台会发热(现可能已修复)
针对以上问题,去年年中的时候就决定重新编写一套新的 SVGA 播放器给内部使用,希望能够解决上面三个问题。经过一年多的修改和生产环境验证,目前个人认为可以分享出来和使用 SVGA 的朋友一起使用(说来也巧,官方的 Lite 版播放器差不多也是这个时候开始编写,有点撞车)
处理痛点
问题 1
针对第一个问题,我们假设一个场景:载入一个 SVGA 文件后播放两段动画,第一段先播 10 帧,然后播放完成后再播 10 帧
// 官方 SVGA Web 播放器播放两段动画. const parser = new SVGA.Parser('#stage') const player = new SVGA.Player('#stage') player.onload('/file.svga', videoItem => { player.setVideoItem(videoItem) let isSection2Played = false player.onFinished(() => { if (!isSection2Played) { playSection2() isSection2Played = true } else { console.log('done') } }) playSection1() }) function playSection1 () { player.startAnimationWithRange({ location: 0, length: 10 }) } function playSection2 () { player.startAnimationWithRange({ location: 10, length: 10 }) }
我们实践下来当业务复杂时,需要外层再写一层封装来帮助进行流程控制,否则会比较别扭,要手工使用变量维护播放状态.
对于业务来说,个人理想中的 API 应该是类似这样:
// SVGAPlus 播放两段动画. import { SVGAPlus } from '@svgaplus/core' const player = new SVGAPlus({ element: document.querySelector('#stage'), buffer: await SVGAPlus.loadSvgaFile('/file.svga') }) await player.init() await player.playOnce(0, 9) await player.playOnce(10, 19) console.log('done')
将状态控制保持在播放器内部,使得业务在使用时无需自行维护一套状态,同时 API 应当保持一种形式同步,来消弱心智负担.
问题 2
针对第二个问题,其实不需要做什么特殊处理,也不用提供缩放 API,舞台( Canvas )的缩放让业务方在外部直接使用 css 进行设置,这样行为和 是完全一致的,毕竟对于切图仔来讲图片的 css 样式是刻在基因中的,设计其他 API 反而有点复杂.
<!-- 直接使用样式表控制尺寸才是追吼的. --> <canvas width="1200" height="1800" style="width: 300px"></canvas>
问题 3
针对第三个问题,最早遇到这个问题时个人在业务中复写了 requestAnimationFrame 方法,在 IOS 平台下限制执行频率来降低手机发热;当自己在编写这套新的播放器时,用于控制绘制的 Ticker 本身可设定帧率,所以原本打算使用这个特性为 IOS 做限制,但使用下来发现就算使用满帧率绘制也没有发生严重的发热问题,所以应该是官方某个版本中有些小问题,现在应该已经修复了吧.
其他的呢
Worker Parser
我们在业务中发现当页面足够复杂时,页面其他逻辑加上 SVGA 的初始化逻辑可能会使得界面阻塞,所以对 SVGA 的 Parser 加入了 Worker 支持,将吃资源的初始化逻辑放入 Worker 中执行来保证主线程通畅. 这个特性是可选的,因为 Worker 会增加一些初始化时间,在简单的页面中依然可以使用主线程进行解析来保证加载速度.
Pixi Renderer
我们后来加入了一个叫做 PixiRenderer 的模块,使用 Pixi.js 代替原生 Canvas API (个人称之为 VanillaRenderer) 进行渲染,这样就得到了 Pixi 的所有好处,比如 WebGL 加持、自定义 Shader 、滤镜、叠加其他 Sprite 等特性,给胡乱瞎搞打下了坚实的基础.
import { SVGAPlus } from '@svgaplus/core' import { PixiRenderer } from '@svgaplus/renderer.pixi' import * as Filters from 'pixi-filters' const player = new SVGAPlus({ element: document.querySelector('#stage'), buffer: await SVGAPlus.loadSvgaFile('/file.svga'), renderer: PixiRenderer }) await player.init() // Pixi 的 App 和 Container 可以这样访问: // player.renderer.pixiApp // player.renderer.pixiContainer // 瞎搞个滤镜瞅瞅. player.renderer.pixiContainer.filters = [ new Filters.RGBSplitFilter() ] player.play()
更多的信息
官方播放器给的信息有时候不够用,所以这次将更多的信息暴露了出来.
SVGAPlus
个人起名为 SVGAPlus,只是希望解决一些问题,毕竟平平淡淡才是真,解决问题才算好.
欢迎大家一起贡献: https://github.com/SVGAPlus/SVGAPlus
请注意
SVGAPlus 没有实现 SVGA 的一些特性,比如音频播放,因为我们在播放音频 + 视频的场景都是直接使用 .mp4 ,个人也比较推荐使用视频这样的通用做法.