npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

custom-svga

v2.3.0

Published

A lightweight, tree-shakable SVGA animation player for Vue 3 & modern web. Isomorphic inline worker parsing with main-thread MockWorker fallback, in-flight sprite/text replacement. Zero UI framework deps.(面向 Vue3 与现代浏览器的轻量 SVGA 动画播放器。同构内联 Worker 解析 + 主线程

Readme

custom-svga

轻量、可摇树的 SVGA 动画播放器,面向 Vue3、现代浏览器与移动端。

⭐ 如果这个项目对你有帮助,欢迎在 GitHub 上点亮 Star 支持!

GitHub stars

极致轻量 · 移动端更高效 · 同构内联 Worker 解析 · 动态精灵/文本替换 · 零 UI 框架依赖
  • 同构内联 Worker 解析:真实 Web Worker 可用时自动在 Worker 内解析;不支持/受限时主线程 MockWorker(沙箱 new Function)复用同一份代码降级,零配置。
  • 构建期内联:解析代码在打包时编译为字符串内联进主包,单入口、无独立 worker 产物。
  • 动态精灵/文本替换、海报、正/逆序、缩放适配、帧区间/播放至指定帧、循环与结束行为。
  • 零 UI 框架依赖,仅构建期依赖 fflateesm ~40KB / gzip ~13KB(小于官方 svga.lite)。

极致轻量 · 为移动端而生

  • 体积极小、冷启动快:单包 gzip 仅 ~13KB、零运行时依赖,不引入任何 UI 框架与重型运行时,首帧可快速拉起,特别适合移动端低端机与包体敏感场景。
  • 移动端更省电省内存
    • 多实例共享一个 RAF,按时间预算公平轮转,单实例卡顿不拖垮整页(useSharedRAF);
    • Path2D 预缓存 / 帧位图缓存 / 动态文本位图化,把每帧重计算与重复光栅化降到最低,显著降低 GPU/CPU 负载;
    • 视口不可见时自动暂停播放useIntersectionObserver),滚动列表外动画零开销。
  • 按需装配:解析与渲染均为函数式、可摇树(sideEffects: false),按打包工具自动剔除未用代码,最终体积进一步收缩。

安装

npm i custom-svga
# 或 CDN <script src="https://unpkg.com/custom-svga"></script> → window.CustomSvga

快速开始(Vue3)

<script setup>
import { ref, onBeforeUnmount } from 'vue'
import { Player, Parser } from 'custom-svga'

const canvas = ref(null)
let player = null

async function load() {
  player?.destroy()
  player = new Player(canvas.value, { loop: 0, scaleMode: 'AspectFit' })
  player.onReady(() => player.play())
  const movie = await Parser.loadUrl('https://example.com/anim.svga')
  player.setVideoItem(movie)
}
onBeforeUnmount(() => player?.destroy())
</script>

<template><canvas ref="canvas"></canvas></template>

Player

构造函数

new Player(element, config?)
// element: HTMLCanvasElement | HTMLDivElement(div 容器模式会自动创建并管理画布)

方法

| 方法 | 说明 | |---|---| | setVideoItem(MovieData \| VideoUrlSource) | 装载动画数据(Parser 解析结果,或 { url, ...解析选项 } 对象——内部自动 Parser.loadUrl,解析选项平铺:useWorker/cache/useImageBitmap) | | play(config?) | 开始/恢复播放,可带本次覆盖配置 | | playRange(start, end, config?) | 播放指定帧区间 | | pause() | 暂停,保持当前帧 | | stop() | 停止并应用配置的结束行为(fillMode:末帧/首帧/清空) | | skipRender(skip?) | 切换/设置是否跳过渲染(帧时间轴仍推进);不传参则切换 | | stepToFrame(frame, andPlay?) | 跳转到指定帧 | | stepToPercentage(p, andPlay?) | 跳转到进度(0~1) | | setImage(img \| url, key) | 动态替换图层图片 | | setText(text \| DynamicText, key) | 动态替换图层文本 | | clearDynamic(key?) | 清除动态替换,不传 key 则清空全部 | | renderPoster(url) | 外链图片作静态海报渲染到画布 | | resize() | 按 DOM 容器与 DPR 校准画布尺寸 | | clear() | 清空当前动画数据与画布(保留实例,可再 setVideoItem) | | destroy() | 彻底销毁实例(画布/监听/内存),需重新 new Player |

事件

| 事件 | 回调参数 | 触发时机 | |---|---|---| | onReady | MovieData? | 数据就绪 | | onError | Error | 运行/加载异常 | | onStart / onPause / onStop | - | 对应生命周期 | | onLoopEnd | - | 每轮循环结束(含最后一轮) | | onEnd | - | 播放彻底结束(仅当配置有限 loop 达到上限停摆时触发;loop: 0 无限循环与手动 stop/pause 不触发) | | onProcess | frame: number | 每推进一帧 | | onPoster | PosterEventInfo | 海报展示/隐藏切换 |

Parser

静态方法(多个调用共用同一共享 Worker 线程):

Parser.load(source, options?)          // source: url | File | Blob
Parser.loadUrl(url, options?)
Parser.loadFile(file, options?)
Parser.parseBuffer(buffer, options?)
Parser.delete(key)                     // 清除单个缓存
Parser.clear()                         // 清空全部缓存(保留共享 Worker)
Parser.destroy()                       // 清空全部缓存并释放共享 Worker(下次解析按需重建)

实例化(每个实例持有独立 Worker,解析配置在实例化时一次性声明,方法不再逐次传 options):

const parser = new Parser({ useWorker: true, cache: true, autoClose: false })
await parser.loadUrl(url)              // 与静态签名一致,但走独立 Worker 线程
await parser.load(file)                // loadUrl / loadFile / parseBuffer 同理
parser.delete(key) / parser.clear() / parser.destroy()

内存/文件信息(独立导出,内部复用全局缓存):

import { queryMemoryInfo } from 'custom-svga'
await queryMemoryInfo(url, true)       // log=true 打印表格

options / ParserConfiguseWorker(默认 true,false 强制主线程 MockWorker 解析)、useImageBitmap(默认 false,解码为 ImageBitmap)、cache(默认 true,LRU 缓存 20 条)。缓冲解析(loadFile/parseBuffer)需显式传 movieDataId 才会写入缓存(匿名缓冲无稳定键,不缓存以免污染 LRU);实例化时 movieDataId 也是实例级缓存键。实例额外支持 autoClose(默认 false):为 true 时每次解析成功即自动释放当前 Worker(适合低频/一次性解析,用完即释放线程)。

PlayConfig

| 配置 | 默认 | 说明 | |---|---|---| | loop | 0 | 循环次数,0=无限 | | fps | 源文件 | 覆盖帧率 | | fillMode | forwards | 播放结束行为:forwards末帧 / backwards首帧 / clear清空 | | startFrame / endFrame | - | 播放区间 | | direction | forward | 播放方向:forward / backward(逆序) | | scaleMode | AspectFit | SCALE_MODEAspectFit / AspectFill / Fill | | useIntersectionObserver | true | 视口不可见优化 | | invisibleBehavior | pause | 不可见时 pause(停走)或 skip(仅跳绘制,帧仍走) | | useSharedRAF | true | 多实例共享一个 RAF | | cacheFrames / maxCacheFrames | false / 100 | 帧缓存(位图化),适合大量 Shape/Clip | | defaultPoster | - | 默认静态海报(未播放/出错/clear 时显示) | | autoPlay | false | setVideoItem 装载完成后是否立即自动播放 |

同构 Worker 说明

  • 解析代码唯一一份(svga.worker),打包时由构建插件编译为 IIFE 字符串内联。
  • useWorker: true 且浏览器支持 → new Worker(new Blob([code])) 真子线程解析。
  • Worker 不可用 / 创建失败 → new MockWorker(code):沙箱 new Function('self', 'globalThis', 'postMessage', 'importScripts', code) 主线程同构解析。
  • 解析带 30s 超时与 onerror 自愈兜底,避免 Promise 悬挂。静态方法共用共享 Worker;new Parser() 每个实例持有独立 Worker,useWorker 模式切换会重建 Worker。
  • ⚠️ MockWorker 依赖 new Function,严格 CSP(无 unsafe-eval)环境下无法降级;真实 Worker 需允许 blob:

动态替换示例

player.setText({ text: 'Hello', size: '18px', color: '#fff' }, 'avatar') // 替换图层 avatar 为文本
await player.setImage('https://example.com/face.png', 'avatar')          // 或替换为图片
player.clearDynamic('avatar')                                            // 清除单个
player.clearDynamic()                                                    // 清空全部

常见问题

  • 停止后没回到首帧?stop() 按配置的 fillMode 决定末帧/首帧/清空;如需固定首帧请设 fillMode:'backwards'
  • 暂停与停止区别pause() 停在当前帧;stop() 结束并应用 fillMode
  • 体积:esm/cjs/umd 单文件,fflate 已内联进 worker 字符串,运行时不需额外依赖。

License

MIT © DreamLife(Zhou)

如果觉得好用,欢迎 Star ⭐,也欢迎提交 Issue / PR。