1 | <img src="https://pag.art/img/readme/logo.png" alt="PAG Logo" width="474"/>
|
2 |
|
3 | [官网](https://pag.art) | [English](./README.md) | 简体中文 | [Weblite版本](./web/lite) | [小程序版本](./web/wechat) | [小程序lite版本](./web/lite/wechat)
|
4 |
|
5 | ## 介绍
|
6 |
|
7 | libpag 是 PAG (Portable Animated Graphics) 动效文件的渲染 SDK,目前已覆盖几乎所有的主流平台,包括:iOS, Android, macOS,
|
8 | Windows, Linux, 以及 Web 端。
|
9 |
|
10 | ## 特性
|
11 |
|
12 | - Web 平台能力适配,支持 libpag 全能力
|
13 | - 基于 WebAssembly + WebGL
|
14 |
|
15 | ## 快速开始
|
16 |
|
17 | PAG Web 端,由 libpag.js + libpag.wasm 文件组成。
|
18 |
|
19 | ### Browser(推荐)
|
20 |
|
21 | 直接使用 `<script>` 引入,`libpag` 会被注册为一个全局变量
|
22 |
|
23 | 对于生产环境我们推荐使用一个明确的版本号,以避免新版本带来不可预期的影响
|
24 |
|
25 | ```html
|
26 | <script src="https://cdn.jsdelivr.net/npm/libpag@4.1.8/lib/libpag.min.js"></script>
|
27 | ```
|
28 |
|
29 | 你可以在公共 CDN [cdn.jsdelivr.net/npm/libpag/](https://cdn.jsdelivr.net/npm/libpag/) 浏览 NPM 包内的内容,同时你也可以使用 `@latest` 将版本指定为最新的稳定版。
|
30 |
|
31 | 也可以使用其他同步 NPM 的公共 CDN 如 [unpkg](https://unpkg.com/libpag@latest/lib/libpag.min.js)
|
32 |
|
33 | ```html
|
34 | <canvas class="canvas" id="pag"></canvas>
|
35 | <script src="https://cdn.jsdelivr.net/npm/libpag@latest/lib/libpag.min.js"></script>
|
36 | <script>
|
37 | window.onload = async () => {
|
38 | // 实例化 PAG
|
39 | const PAG = await window.libpag.PAGInit();
|
40 | // 获取 PAG 素材数据
|
41 | const buffer = await fetch('https://pag.art/file/like.pag').then((response) => response.arrayBuffer());
|
42 | // 加载 PAG 素材为 PAGFile 对象
|
43 | const pagFile = await PAG.PAGFile.load(buffer);
|
44 | // 将画布尺寸设置为 PAGFile的尺寸
|
45 | const canvas = document.getElementById('pag');
|
46 | canvas.width = pagFile.width();
|
47 | canvas.height = pagFile.height();
|
48 | // 实例化 PAGView 对象
|
49 | const pagView = await PAG.PAGView.init(pagFile, canvas);
|
50 | // 播放 PAGView
|
51 | await pagView.play();
|
52 | };
|
53 | </script>
|
54 | ```
|
55 |
|
56 | 调用 libpag.js 上的 `PAGInit()` 方法时,默认会加载 libpag.js 同一目录下的 libpag.wasm 文件。如果你希望把 libpag.wasm 放在其他目录下,则可以使用 `locateFile` 将 libpag.wasm 的路径返回给 `PAGInit()` 方法。如下
|
57 |
|
58 | ```js
|
59 | const PAG = await window.libpag.PAGInit({
|
60 | locateFile: () => {
|
61 | if (location.host === 'dev.pag.art') {
|
62 | // development environment
|
63 | return 'https://dev.pag.art/file/libpag.wasm';
|
64 | } else {
|
65 | // production environment
|
66 | return 'https://pag.art/file/libpag.wasm';
|
67 | }
|
68 | },
|
69 | });
|
70 | ```
|
71 |
|
72 | ### EsModule
|
73 |
|
74 | ```bash
|
75 | $ npm i libpag
|
76 | ```
|
77 |
|
78 | ```js
|
79 | import { PAGInit } from 'libpag';
|
80 |
|
81 | PAGInit().then((PAG) => {
|
82 | // 实例化 PAG
|
83 | });
|
84 | ```
|
85 |
|
86 | **使用 ESModule 引入的方式需要注意,像 Webpack 和 Rollup 等打包工具是默认没有打包 .wasm 文件的。也就是说如果你的项目时 Vue / React 这类使用脚手架构建的项目需要把 node_modules 下的 libpag/lib/libpag.wasm 文件打包到最终产物中,并且使用 `locateFile` 钩子返回 libpag.wasm 文件的路径,这样才能确保在网络请求中能加载到 libpag.wasm 文件**
|
87 |
|
88 | npm package 中提供了多种构建产物,可以阅读 [这里](./doc/develop-install.md) 了解不同目录下产物的差别。
|
89 |
|
90 | Demo 项目提 [pag-web](https://github.com/libpag/pag-web) 供了简单的接入示例和 Vue / React / PixiJS 等配置示例, 可以点击 [这里](https://github.com/libpag/pag-web) 查看。
|
91 |
|
92 | 更多的 API 接口可以阅读 [API 文档](https://pag.art/api.html#/apis/web/)。
|
93 |
|
94 | ## 浏览器兼容性
|
95 |
|
96 | | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Chrome for Android | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br/>Safari on iOS | QQ Browser Mobile |
|
97 | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ----------------- |
|
98 | | Chrome >= 69 | Safari >= 11.3 | Android >= 7.0 | iOS >= 11.3 | last 2 versions |
|
99 |
|
100 | 更多版本的兼容工作正在进行中
|
101 |
|
102 | **以上的兼容表仅代表可以运行的兼容性。对于有移动端接入需要的用户,需要阅读一下这篇[兼容性情况](./doc/compatibility.md)的文章**
|
103 |
|
104 | ## 使用指南
|
105 |
|
106 | ### 垃圾回收
|
107 |
|
108 | 因为 libpag 基于 WebAssembly 做跨端应用,所以从 WebAssembly 中取出来的对象,基本都是 C++ 的指针,所以 JavaScript 的 GC 是无法释放这一部分内存的。所以,当不需要使用 libpag 产生的对象时,需要调用对象上的 destroy 方法将其释放掉。
|
109 |
|
110 | ### 渲染相关
|
111 |
|
112 | #### 首帧渲染
|
113 |
|
114 | `PAGView.init` 默认会进行首帧渲染,如不需要可以在 `PAGView.init` 的第三个参数传入 `{ firstFrame: false }` 取消首帧渲染。
|
115 |
|
116 | 在进行首帧渲染的过程中,当 PAG 动画文件中存在 BMP 预合成(下文说明如何确认 PAG 文件是否存在 BMP 预合成)时,会调用 VideoReader 模块。
|
117 |
|
118 | 因为 VideoReader 模块在 Web 平台依赖于 VideoElement,所以在部分移动端场景下,PAGView.init 不是在用户交互产生的调用链中,可能会存在不允许播放导致无法正常渲染画面的问题。
|
119 |
|
120 | 当出现这种情况,我们推荐使用 `PAGView.init(pagFile, canvas, { firstFrame: false }) ` 提前初始化 PAGView 并且取消首帧渲染,然后在用户交互产生的调用链中再调用 `pagView.play()` 进行画面渲染。
|
121 |
|
122 | #### PAG 渲染尺寸
|
123 |
|
124 | 在 Web 平台上,设备像素分辨率与 CSS 像素分辨率是不同的,而它们之比被称为 `devicePixelRatio`。所以当我们需要显示 CSS 像素 1px 时, 需要 1px \* `devicePixelRatio` 的渲染尺寸。
|
125 |
|
126 | PAG 默认会对 Canvas 在屏幕中的可视尺寸进行缩放计算后进行渲染,因此会对 Canvas 的宽高以及 `style` 产生副作用。如果希望 PAG 不对 Canvas 产生副作用, 可以在 `PAGView.init` 的第三个参数传入 `{ useScale: false }` 来取消缩放。
|
127 |
|
128 | #### PAGView 尺寸过大
|
129 |
|
130 | 为了高清的渲染效果,PAGView 默认会按照 Canvas 尺寸 \* `devicePixelRatio` 作为实际渲染尺寸。
|
131 | 受设备自身性能影响 WebGL 的最大渲染尺寸可能各不相同。会出现渲染尺寸过大导致白屏的情况。
|
132 |
|
133 | 建议移动端下,实际渲染尺寸不大于 2560px。
|
134 |
|
135 | #### 多个 PAGView 实例场景
|
136 |
|
137 | 首先,因为 PAG Web 版是单线程的 SDK,所以我们不建议**同屏播放多个 PAGView**。
|
138 |
|
139 | 对于有多个 PAGView 实例的场景,我们需要先知道,浏览器环境中 WebGL 活跃的 context 数量是有限制的,Chrome 是 16 个,Safari 是 8 个。因为有这个限制存在,我们应当及时使用 `destroy` 回收无用的 PAGView 实例和移除 Canvas 的引用。
|
140 |
|
141 | 以下是特殊场景的解决方法,不推荐使用:
|
142 |
|
143 | 如果你需要在 Chrome 浏览器中同屏存在多个 PAGView 实例且不需要在 Safari 上使用,可以尝试使用 canvas2D 模式,需要在 `PAGView.init` 的时候传入 `{ useCanvas2D: true }` 。这个模式下,会用一个 WebGL 当作渲染器,然后往多个 canvas2D 分发画面,从而规避 WebGL 活跃 context 数量的限制。
|
144 |
|
145 | 因为 Safari 上 [`CanvasRenderingContext2D.drawImage()`](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/drawImage) 的性能很差,所以我们不推荐在 Safari 上使用这个模式。
|
146 |
|
147 | #### 注册解码器
|
148 |
|
149 | 对于“用户与页面交互之后才可以使用 Video 标签进行视频播放”规则的限制,PAG Web 版提供软件解码器注入接口 ` PAG.registerSoftwareDecoderFactory()`。
|
150 |
|
151 | 注入软件解码器后,PAG 会调度软件解码器去对 BMP 预合成进行解码与上屏,从而实现部分平台进入页面自动播放的功能。
|
152 |
|
153 | 推荐解码器接入:https://github.com/libpag/ffavc/tree/main/web
|
154 |
|
155 | 已知“用户与页面交互之后才可以使用 Video 标签进行视频播放”限制的平台有:移动端微信浏览器,iPhone 省电模式,部分 Android 浏览器。
|
156 |
|
157 | ## 关于 BMP 预合成
|
158 |
|
159 | 可以下载 [PAGViewer](https://pag.art/docs/install.html) 打开 PAG 文件,点击"视图"->"显示 编辑面板",在编辑面板中我们能看到 Video 的数量,当 Video数量大于 0 时,即为 PAG 动画文件中存在 BMP 预合成。
|
160 |
|
161 | ## Roadmap
|
162 |
|
163 | Web SDK 未来能力支持规划可以点击 [这里](https://github.com/Tencent/libpag/wiki/PAG-Web-roadmap) 查看
|
164 |
|
165 | ## 参与开发
|
166 |
|
167 | ### 前置工作
|
168 |
|
169 | 需要确保已经可编译 C++ libpag 库,并且安装 [Emscripten 套件](https://emscripten.org/docs/getting_started/downloads.html) 和 Node 依赖
|
170 |
|
171 | ```bash
|
172 | # 安装Node依赖
|
173 | $ npm install
|
174 | ```
|
175 |
|
176 | ### 开发流程
|
177 |
|
178 | 执行 `build.sh debug` 来获得 `libpag.wasm` 文件
|
179 |
|
180 | ```bash
|
181 | # web/script目录下
|
182 | $ cd script
|
183 | # 添加执行权限
|
184 | $ chmod +x ./build.sh
|
185 | # 打包
|
186 | $ ./build.sh debug
|
187 | ```
|
188 |
|
189 | 打包 Typescript 文件,修改 Typescript 文件会自动打包到 Javascript 文件
|
190 |
|
191 | ```bash
|
192 | # web目录下
|
193 | $ npm run dev
|
194 | ```
|
195 |
|
196 | 启动 HTTP 服务
|
197 |
|
198 | ```bash
|
199 | # web目录下
|
200 | $ npm run server
|
201 | ```
|
202 |
|
203 | Chrome 浏览器打开 `http://localhost:8081/demo/index.html` 即可看到效果
|
204 |
|
205 | 需要断点调试时,可以安装 [C/C++ DevTools Support (DWARF)](https://chrome.google.com/webstore/detail/cc%20%20-devtools-support-dwa/pdcpmagijalfljmkmjngeonclgbbannb),并打开 Chrome DevTools > 设置 > 实验 > 勾选「WebAssembly Debugging: Enable DWARF support」选项启用 SourceMap 支持。现在就可以在 Chrome DevTools 中对 C++ 文件进行断点调试了。
|
206 |
|
207 | #### 注意点
|
208 |
|
209 | 在使用 `build.sh` 编译 `libpag.wasm` 时,因为 `emscripten` 与系统的 std 库有兼容问题,所以屏蔽了 undefined symbols 报错。
|
210 |
|
211 | ```shell
|
212 | # build.sh
|
213 | emcc -s ERROR_ON_UNDEFINED_SYMBOLS=0
|
214 | ```
|
215 |
|
216 | 编译过程中需要留意是否有std库兼容外的 warning 信息,避免 undefined symbols 的错误在运行时才暴露出来。
|
217 |
|
218 | ### 生产流程
|
219 |
|
220 | 执行 `build.sh` 脚本
|
221 |
|
222 | ```bash
|
223 | # web/script目录下
|
224 | $ cd script
|
225 | # 添加执行权限
|
226 | $ chmod +x ./build.sh
|
227 | # 打包
|
228 | $ ./build.sh
|
229 | ```
|
230 |
|
231 | ### CLion 编译
|
232 |
|
233 | 创建一个新的 profile,然后使用下面的 **CMake options**(位置在 **CLion** > **Preferences** > **Build, Execution, Deployment** > **CMake**)
|
234 |
|
235 | ```
|
236 | -DCMAKE_TOOLCHAIN_FILE=path/to/emscripten/emscripten/version/cmake/Modules/Platform/Emscripten.cmake
|
237 | ```
|
238 |
|
239 | ### 测试流程
|
240 |
|
241 | 打包生产版本
|
242 |
|
243 | ```bash
|
244 | $ cd script & ./build.sh
|
245 | ```
|
246 |
|
247 | 启动测试 HTTP 服务
|
248 |
|
249 | ```bash
|
250 | $ npm run server
|
251 | ```
|
252 |
|
253 | 启动 cypress 测试
|
254 |
|
255 | ```bash
|
256 | $ npm run test
|
257 | ```
|