使用了一年多的另一个 SVGA Web 播放器现分享给大家

2020-08-24 17:09:02 +08:00
 LancerComet

SVGA

SVGA ( https://svga.io) 是 YY 研发的一套很棒的动画方案,这个方案在公司内大规模使用,因为是一套从设计到研发一套打通的方案,所以效率和整体体验都算不错.

不过官方的 Web 版播放器使用起来有几个痛点:

  1. SVGA 最初应该没有考虑太多的流程控制,不过现实业务中经常性的是通过多个 SVGA 组合进行逻辑动画播放,比如 A 播 XX 帧之后再播放 B,然后再播放 C ;或者 A 原地 n 到 m 帧循环,用户点击后跳转到 p 帧播一次;官方的 API 在处理这个情景上不是那么自然,当场景复杂了之后需要写额外的封装处理.

  2. 官方播放器提供了多种缩放模式,但使用起来有些别扭,而且没有办法使用 CSS 样式控制舞台尺寸,当使用 CSS 设定宽高时舞台内容会出现没有规律的奇怪变形,灵活性稍微差了点,我们平常使用 CSS 的 scale 来处理.

  3. 某些版本的 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 ,个人也比较推荐使用视频这样的通用做法.

2437 次点击
所在节点    分享创造
3 条回复
LawlietZ
2020-08-25 10:28:48 +08:00
不错
LawlietZ
2020-08-25 10:33:16 +08:00
话说这种可以实现透明视频,svga 是新格式吧,该怎么导出呢
LancerComet
2020-08-25 11:00:55 +08:00
@LawlietZ 是可以实现透明效果,SVGA 本质就是一堆 PNG / SVG 然后再加上帧信息,通过 AE 做完之后使用插件导出 https://svga.io/designer.html

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/701013

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX