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 版播放器差不多也是这个时候开始编写,有点撞车)
针对第一个问题,我们假设一个场景:载入一个 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 应当保持一种形式同步,来消弱心智负担.
针对第二个问题,其实不需要做什么特殊处理,也不用提供缩放 API,舞台( Canvas )的缩放让业务方在外部直接使用 css 进行设置,这样行为和
<!-- 直接使用样式表控制尺寸才是追吼的. -->
<canvas width="1200" height="1800" style="width: 300px"></canvas>
针对第三个问题,最早遇到这个问题时个人在业务中复写了 requestAnimationFrame 方法,在 IOS 平台下限制执行频率来降低手机发热;当自己在编写这套新的播放器时,用于控制绘制的 Ticker 本身可设定帧率,所以原本打算使用这个特性为 IOS 做限制,但使用下来发现就算使用满帧率绘制也没有发生严重的发热问题,所以应该是官方某个版本中有些小问题,现在应该已经修复了吧.
我们在业务中发现当页面足够复杂时,页面其他逻辑加上 SVGA 的初始化逻辑可能会使得界面阻塞,所以对 SVGA 的 Parser 加入了 Worker 支持,将吃资源的初始化逻辑放入 Worker 中执行来保证主线程通畅. 这个特性是可选的,因为 Worker 会增加一些初始化时间,在简单的页面中依然可以使用主线程进行解析来保证加载速度.
我们后来加入了一个叫做 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,只是希望解决一些问题,毕竟平平淡淡才是真,解决问题才算好.
欢迎大家一起贡献: https://github.com/SVGAPlus/SVGAPlus
SVGAPlus 没有实现 SVGA 的一些特性,比如音频播放,因为我们在播放音频 + 视频的场景都是直接使用 .mp4 ,个人也比较推荐使用视频这样的通用做法.
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.