octopus-svga
v2.0.0
Published
高性能SVGA动效播放器,支持Web/微信小程序/支付宝小程序/抖音小程序,设计目标是 解析速度更快、体积更小、性能更高、兼容性更高、功能更丰富。
Maintainers
Readme
Octopus Svga
这是一个 SVGA 在移动端 Web/小程序 上的播放器,设计它的目标是 解析速度更快、体积更小、性能更高、兼容性更高、功能更丰富。
实现
[x] 兼容 Android 4.4+ / iOS 9+
[x] 整体大小 ~80Kb,核心部分大小 ~50Kb(解析器 + 播放器),小于 SVGAPlayer-Web-Lite
[x] 实现多端兼容,目前支持 H5、微信小程序、支付宝小程序、抖音小程序
[x] 优化 protobuf 解析器体积,增强二进制解析速度
[x] 支持 双缓冲渲染机制 + 指数退避算法 提升渲染性能
[x] 支持基于 SVGA 格式的 模版海报 绘制 (需配合 png 图片生成器使用)
[x] 支持动效文件 管理器 (支持自定义下载、解压、解析的策略)
[x] 支持动效文件 编辑器 (支持图片和二维码生成和替换,也可以自定义绘图)
[x] 内置 二维码生成器
[x] 内置 png 图片生成器
PS:目前兼容的场景仅 H5、微信小程序、支付宝小程序、抖音小程序,理论上仅需放开限制即可兼容更多小程序场景以及鸿蒙元服务。
暂未实现
- [ ] GPU 加速渲染(WebGL/WebGPU渲染),并支持用户手动切换渲染模式
- [ ] Web 端支持 IndexDB(扩展 plugin-fsm 插件,使其操作方式与文件操作类似)
- [ ] 支持 IntersectionObserver(考虑到体积大小,可能作为外部依赖支持,比如用户实现一个自定义的platform的方式)
注意事项
- 不支持播放 SVGA 1.x 格式
- 不支持声音播放
架构设计

安装
NPM
npm i octopus-svga -S注意:如果你使用 ESM 模块,需要安装 npm i octopus-platform -S。
配置项
PlayerConfigOptions
const enum PLAYER_FILL_MODE {
// 播放完成后停在首帧
FORWARDS = "forwards",
// 播放完成后停在尾帧
BACKWARDS = "backwards",
// 播放完成后清空
NONE = "none",
}
const enum PLAYER_PLAY_MODE {
// 顺序播放
FORWARDS = "forwards",
// 倒序播放
FALLBACKS = "fallbacks",
}
const enum PLAYER_CONTENT_MODE {
/**
* 缩放图片填满 Canvas,图片可能出现变形
*/
FILL = "fill",
/**
* 等比例缩放至整张图片填满 Canvas,不足部分留白
*/
ASPECT_FIT = "aspect-fit",
/**
* 等比例缩放至图片填满 Canvas,超出部分不展示
*/
ASPECT_FILL = "aspect-fill",
/**
* 图片对齐 Canvas 中心,超出部分不展示
*/
CENTER = "center",
}
export interface PlayerConfig {
/**
* 循环次数,默认值 0(无限循环)
*/
loop: number;
/**
* 最后停留的目标模式,类似于 animation-fill-mode,默认值 forwards。
*/
fillMode: PLAYER_FILL_MODE;
/**
* 播放模式,默认值 forwards
*/
playMode: PLAYER_PLAY_MODE;
/**
* 填充模式,类似于 content-mode。
*/
contentMode: PLAYER_CONTENT_MODE;
/**
* 开始播放的帧数,默认值 0
*/
startFrame: number;
/**
* 结束播放的帧数,默认值 0
*/
endFrame: number;
/**
* 循环播放的开始帧,默认值 0
*/
loopStartFrame: number;
}
export type PlayerConfigOptions = Partial<PlayerConfig> & {
/**
* 主屏,播放动画的 Canvas 元素
*/
container: string;
/**
* 副屏,播放动画的 Canvas 元素
*/
secondary?: string;
};PosterConfigOptions
export interface PosterConfig {
/**
* 主屏,绘制海报的 Canvas 元素
*/
container: string;
/**
* 填充模式,类似于 content-mode。
*/
contentMode: PLAYER_CONTENT_MODE;
/**
* 绘制成海报的帧,默认是0。
*/
frame: number;
}
export type PosterConfigOptions = Partial<PosterConfig>;使用
简单使用
<canvas id="container"></canvas>
<!-- <canvas id="secondary"></canvas> -->import { Parser, Player } from "octopus-svga";
const player = new Player();
await player.setConfig({
// 主屏Canvas选择器
container: "#container",
// 辅助Canvas选择器(不设置默认会使用离屏渲染代替)
// secondary: "#secondary",
});
// 加载并解析svga文件
const videoItem = await Parser.load("xx.svga");
// 绑定事件方法
player.onStart = () => console.log("onStart");
player.onResume = () => console.log("onResume");
player.onPause = () => console.log("onPause");
player.onStop = () => console.log("onStop");
player.onProcess = (percent, frame) => console.log("onProcess", percent, frame);
player.onEnd = () => console.log("onEnd");
await player.mount(videoItem);
// 开始播放动画
player.start();播放器方法
// 开始播放,会重置状态
player.start();
// 停止播放,会重置状态
player.stop();
// 重新播放,不会重置状态
player.resume();
// 暂停播放,不会重置状态
player.pause();指定帧/百分比进度播放
// 从第 10 帧开始播放
player.stepToFrame(10, true);
// 从 50% 进度开始播放
player.stepToPercentage(0.5, true);Parser 解析器
// 加载svga文件(这是一个复合功能,包含以下三个功能)
await Parser.load(url);
// 下载svga文件
await Parser.download(url);
// 解压svga文件
Parser.decompress(buff);
// 解析svga文件
Parser.parseVideo(buff, url, needDecompress);VideoEditor 动效编辑器配合 Poster 海报
可通过修改解析后的数据元,从而实现修改元素、插入动态元素功能
<img class="poster" src="" />import {
Parser,
Poster,
VideoEditor,
createImageDataUrl,
} from "octopus-svga";
const videoItem = await Parser.load("xx.svga");
const poster = new Poster(750, 1180);
const videoEditor = new VideoEditor(poster.painter, poster.resource, videoItem);
// 替换元素
// mode A 为追加新图片 R 为替换已有图片
videoEditor.setImage("replace_001", "https://assets.xxx.com/frontend/xxx.png");
const context = videoEditor.getContext();
// 动态元素
context.font = "30px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillStyle = "#000";
context.fillText(
"hello svga!",
context.clientWidth / 2,
context.clientHeight / 2
);
videoEditor.setCanvas("dynamic_001", context, {
mode: "A",
width: 375,
height: 400,
});
// 添加二维码图片
videoEditor.setQRCode("qrcode_001", "这是二维码图片的文本内容", { size: 40 });
await poster.mount(videoItem);
poster.draw();
const imageData = poster.toImageData();
// 生成base64格式的png图片
document.querySelector(".poster").src = createImageDataUrl(imageData);VideoManager 动效管理器配合 Player 播放器
import { VideoManager, Player } from "octopus-svga";
const player = new Player();
await player.setConfig({
container: "#xxxx",
});
// mode: fast模式可以尽快播放当前选中的动效文件,whole模式可以等待动效文件全部下载完成。
const videoManager = new VideoManager("fast");
videoManager.prepare(
[
"https://assets.xxx.com/frontend/9ce0cce7205fbebba380ed44879e5660.svga",
"https://assets.xxx.com/frontend/1ddb590515d196f07c411794633e4406.svga",
"https://assets.xxx.com/frontend/9a96c2c0fbe8ec39f0a192e3e1303d22.svga",
"https://assets.xxx.com/frontend/c4b3c4f8a05070352e036e869fc58b2f.svga",
],
0,
3
);
const bucket = videoManager.go(3);
await player.mount(bucket.entity);
player.start();VideoManager 动效管理器(worker 加速)
import { VideoManager, isZlibCompressed } from "octopus-svga";
import { EnhancedWorker } from "../../utils/EnhancedWorker";
const worker = new EnhancedWorker();
// mode: fast模式可以尽快播放当前选中的动效文件,whole模式可以等待动效文件全部下载完成。
const videoManager = new VideoManager("fast", {
// 这里的预进程使用了worker处理,减少主进程卡顿,加快动效文件解压。
// 注意:VideoManager本身不提供worker能力,需要自己实现并接入。
preprocess: (bucket) =>
new Promise((resolve) => {
worker.once(bucket.origin, (data) => resolve(data));
worker.emit(bucket.origin, bucket.origin);
}),
postprocess: (bucket, buff) =>
Parser.parseVideo(
buff,
bucket.origin,
// 检查数据是否已经解压
isZlibCompressed(buff)
),
});具体可参考这里,了解各个端的 Worker 实现。
画布清理方案
- RESIZE: 利用 Canvas 宽高变化会自动清除画布特性。
- CLEAR: 利用 Canvas 上下文的
clearRect方法。
| | Canvas | OffscreenCanvas | | -------------- | ------ | --------------- | | 微信小程序 | RESIZE | RESIZE | | 支付宝小程序 | CLEAR | RESIZE | | 抖音小程序 | RESIZE | CLEAR | | 浏览器 | RESIZE | RESIZE |
其他
SVGA AE 设计插件
