# svga-web2

<p>
  <a href="https://npmcharts.com/compare/svga-web2?minimal=true"><img src="https://img.shields.io/npm/dm/svga-web2.svg?sanitize=true" alt="Downloads"></a>
  <a href="https://www.npmjs.com/package/svga-web2"><img src="https://img.shields.io/npm/v/svga-web2.svg?sanitize=true" alt="Version"></a>
  <a href="https://www.npmjs.com/package/svga-web2"><img src="https://img.shields.io/npm/l/svga-web2.svg?sanitize=true" alt="License"></a>
</p>

一个 [SVGA](http://svga.io/) Web 播放器

## 注意

本项目不是由 [svga.io](http://svga.io/) 官方维护的。
Fork 自 [svga-web](https://github.com/Naeemo/svga-web)，相对于原项目，解决了一些移动端的兼容性问题。

- [x] TypeScript 支持。
- [x] 使用 `howler` 实现的声音播放。

## 安装

### NPM

```sh
yarn add svga-web2

# 或者

npm i svga-web2
```

### CDN

```html
<script src="https://cdn.jsdelivr.net/npm/svga-web2/dist/svga-web2.umd.min.js"></script>
```

## 例子/文档

### Player.set({ 参数 })

| 属性名                     | 说明                     | 类型                   | 默认值     | 备注                                                                                                                                                                                                               |
| -------------------------- | ------------------------ | ---------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| loop                       | 循环次数                 | `number`               | `1`        | 设置为 `0` 时，无限循环                                                                                                                                                                                            |
| fillMode                   | 最后停留的目标模式       | `forwards` `backwards` | `forwards` | 类似于 [css animation-fill-mode](https://developer.mozilla.org/zh-CN/docs/Web/CSS/animation-fill-mode)                                                                                                             |
| playMode                   | 播放模式                 | `forwards` `fallbacks` | `forwards` |
| startFrame                 | 开始播放帧               | `number`               | `0`        |                                                                                                                                                                                                                    |
| endFrame                   | 结束播放帧               | `number`               | `0`        | 设置为 `0` 时，默认为 SVGA 文件最后一帧                                                                                                                                                                            |
| cacheFrames                | 是否缓存帧               | `boolean`              | `false`    | 开启后对已绘制的帧进行缓存，提升重复播放动画性能                                                                                                                                                                   |
| noExecutionDelay           | 是否避免执行延迟         | `boolean`              | `false`    | 开启后使用 `WebWorker` 确保动画按时执行（ [一些情况下浏览器会延迟或停止执行一些任务](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API#Policies_in_place_to_aid_background_page_performance) ） |
| intersectionObserverRender | 是否开启动画容器视窗检测 | `boolean`              | `false`    | 开启后利用 [Intersection Observer API](https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API) 检测动画容器是否处于视窗内，若处于视窗外，停止描绘渲染帧避免造成资源消耗                        |

### 简单使用

```html
<canvas id="canvas"></canvas>
```

```js
import { Downloader, Parser, Player } from 'svga-web2'

const downloader = new Downloader()
const parser = new Parser()
// #canvas 是 HTMLCanvasElement
const player = new Player('#canvas')

;(async () => {
  const fileData = await downloader.get('./xxx.svga')
  const svgaData = await parser.do(fileData)

  player.set({ loop: 1 })

  await player.mount(svgaData)

  player
    // 开始动画事件回调
    .$on('start', () => console.log('event start'))
    // 暂停动画事件回调
    .$on('pause', () => console.log('event pause'))
    // 停止动画事件回调
    .$on('stop', () => console.log('event stop'))
    // 动画结束事件回调
    .$on('end', () => console.log('event end'))
    // 清空动画事件回调
    .$on('clear', () => console.log('event clear'))
    // 动画播放中事件回调
    .$on('process', () => console.log('event process', player.progress))

  // 开始播放动画
  player.start()

  // 暂停播放东湖
  // player.pause()

  // 停止播放动画
  // player.stop()

  // 清空动画
  // player.clear()
})()
```

### 替换元素

你能够通过改变 `svga data` 对应键值的元素

```js
import { Downloader, Parser, Player } from 'svga-web2'

const downloader = new Downloader()
const parser = new Parser()
const player = new Player('#canvas')

;(async () => {
  const fileData = await downloader.get('./xxx.svga')
  const svgaData = await parser.do(fileData)

  const image = new Image()
  image.src = 'https://xxx.com/xxx.png'
  svgaData.images['key'] = image

  await player.mount(svgaData)

  player.start()
})()
```

### 动态元素

你可以通过 `svga data` 插入一些[动态元素](https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/drawImage)

```js
const text = 'hello gg'
const fontCanvas = document.getElementById('font')
const fontContext = fontCanvas.getContext('2d')
fontCanvas.height = 30
fontContext.font = '30px Arial'
fontContext.textAlign = 'center'
fontContext.textBaseline = 'middle'
fontContext.fillStyle = '#000'
fontContext.fillText(
  text,
  fontCanvas.clientWidth / 2,
  fontCanvas.clientHeight / 2
)

const { Downloader, Parser, Player } = SVGA

const downloader = new Downloader()
const parser = new Parser()
const player = new Player('#canvas')

const svgaFile = './svga/kingset.svga'

const fileData = await downloader.get(svgaFile)
const svgaData = await parser.do(fileData)

svgaData.dynamicElements['banner'] = fontCanvas

await player.mount(svgaData)

player.start()
```

为动态元素设置自适应参数 `fit`，参考[例子](examples/11.test-dynamicElement.html).

```js
const video = document.getElementById('video')
const { Downloader, Parser, Player } = SVGA

const downloader = new Downloader()
const parser = new Parser()
const player = new Player('#canvas')

const svgaFile = './svga/kingset.svga'

const fileData = await downloader.get(svgaFile)
const svgaData = await parser.do(fileData)

// fit: "fill", "cover", "contain", "none"
svgaData.dynamicElements['99'] = { source: video, fit: 'fill' }

await player.mount(svgaData)

player.start()
```

### 可复用实例化 Downloader & Parser

```js
import { Downloader, Parser, Player } from 'svga-web2'

const downloader = new Downloader()
const parser = new Parser()

const player1 = new Player('#canvas1')
const player2 = new Player('#canvas2')

const fileData1 = await downloader.get('./1.svga')
const fileData2 = await downloader.get('./2.svga')

const svgaData1 = await parser.do(fileData1)
const svgaData2 = await parser.do(fileData2)

await player1.mount(svgaData1)
await player2.mount(svgaData2)

player1.start()
player2.start()
```

### 销毁实例

```js
const downloader = new Downloader()
downloader.destroy()

const parser = new Parser()
parser.destroy()

const player = new Player('#canvas')
player.destroy()
```

### DB

已下载并解析的数据利用 IndexedDB 进行持久化缓存，下次可避免重复消耗资源对统一 SVGA 下载和解析

```js
import { Downloader, Parser, Player, DB } from 'svga-web2'

const svgaFile = 'test.svga'
let data = void 0
let db = void 0

try {
  db = new DB()
} catch (error) {
  console.error(error)
}

if (db) {
  data = await db.find(svgaFile)
}

if (!data) {
  const downloader = new Downloader()
  const fileData = await downloader.get(svgaFile)
  const parser = new Parser()

  data = await parser.do(fileData)

  // 插入数据
  db && (await db.insert(svgaFile, data))
}

const player = new Player('#canvas')
await player.mount(data)

player.start()
```

### Downloader Cancel (v1.4.0+)

你可以取消下载中的 SVGA 文件请求

```js
downloader
  .get('test.svga')
  .then((fileData) => {
    console.log('下载完成')
  })
  .catch((error) => {
    console.log('catch', error)
  })

setTimeout(() => {
  downloader.cancel() // 或者 downloader.destroy()
}, 1000)
```

## 开发

```shell
# 安装依赖
yarn install

# 开发
yarn start

# 构建
yarn build
```

## LICENSE

[MIT](./LICENSE)
