更新记录
1.0.0(2026-05-18)
c-svga-x 首次发布。
- 新增 uni-app x UTS 组件实现,支持 App-Android / App-iOS。
- 新增 H5/Web 端组件实现,基于
svgaplayerweb 和 renderjs 兼容播放、控制、跳帧和动态对象 API。
- 新增微信小程序端组件实现,基于
svgaplayer-weapp 兼容播放、控制、跳帧和动态对象 API。
- 微信小程序端新增 SVGA 内嵌音频调度,使用
InnerAudioContext 按帧播放音频。
- 对外提供
start、pause、stop 等显式播放控制方法。
- 新增事件:
loaded、finished、frame、percentage。
平台兼容性
uni-app(5.0)
| Vue2 |
Vue2插件版本 |
Vue3 |
Vue3插件版本 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
| √ |
1.0.0 |
√ |
1.0.0 |
√ |
√ |
√ |
√ |
√ |
√ |
- |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| √ |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
uni-app x(5.0)
| Chrome |
Safari |
Android |
iOS |
鸿蒙 |
微信小程序 |
| √ |
√ |
√ |
√ |
- |
√ |
c-svga-x
- c-svga-x uni-app x / UTS svga动画组件
- 原c-svga的升级版本,性能更好
- 对外接口风格参考
c-svga,支持 App-Android / App-iOS 原生播放,并兼容 H5/Web、微信小程序端播放
c-design交流群号:330647926
一、使用示例
vue2/vue3
<template>
<view class="page">
<c-svga-x ref="cSvgaXRef"
:src="src"
:loops="loops"
:auto-play="true"
:clears-after-stop="true"
:fill-mode="fillMode"
:is-on-change="true"
@loaded="onLoaded"
@finished="onFinished"
@frame="onFrame"
@percentage="onPercentage"
@error="onError"
class="svga-box"></c-svga-x>
<view class="content">
<view class="title">切换动画</view>
<view class="btnBox">
<button @click="src='/static/svga/car.svga'" size="mini">car</button>
<button @click="src='/static/svga/kafei.svga'" size="mini">kafei</button>
<button @click="src='/static/svga/huangdikaodao_2.svga'" size="mini">huangdikaodao</button>
</view>
<view class="title">播放控制</view>
<view class="btnBox">
<button @click="play" size="mini">播放</button>
<button @click="pause" size="mini">暂停</button>
<button @click="stop" size="mini">停止</button>
<button @click="clear" size="mini">清空</button>
</view>
<view class="title">画面模式</view>
<view class="btnBox">
<button @click="setContentMode('Fill')" size="mini">Fill</button>
<button @click="setContentMode('AspectFill')" size="mini">AspectFill</button>
<button @click="setContentMode('AspectFit')" size="mini">AspectFit</button>
</view>
<view class="title">跳到指定帧/百分比</view>
<view class="btnBox">
<button @click="stepToFrame" size="mini">跳到30帧</button>
<button @click="stepToPercentage" size="mini">跳到60%</button>
</view>
<view class="title">动态图片/动态文本</view>
<view class="btnBox">
<button @click="setDynamicImage" size="mini">动态图像</button>
<button @click="setDynamicText" size="mini">动态文本</button>
<button @click="clearDynamicObjects" size="mini">清空动态对象</button>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
src: '/static/svga/car.svga',
loops: 0,
fillMode: 'Forward'
}
},
methods: {
player() {
return this.$refs.cSvgaXRef
},
play() {
const player = this.player()
if (player == null) return
if (player.playAnimation != null) return player.playAnimation()
if (player.startAnimation != null) return player.startAnimation()
if (player.play != null) player.play()
},
pause() {
const player = this.player()
if (player == null) return
if (player.pauseAnimation != null) return player.pauseAnimation()
if (player.pause != null) player.pause()
},
stop() {
const player = this.player()
if (player == null) return
if (player.stopAnimation != null) return player.stopAnimation()
if (player.stop != null) player.stop()
},
clear() {
const player = this.player()
if (player != null && player.clear != null) player.clear()
},
setContentMode(mode) {
const player = this.player()
if (player != null && player.setContentMode != null) player.setContentMode(mode)
},
stepToFrame() {
const player = this.player()
if (player != null && player.stepToFrame != null) player.stepToFrame(30, true)
},
stepToPercentage() {
const player = this.player()
if (player != null && player.stepToPercentage != null) player.stepToPercentage(60, true)
},
setDynamicImage() {
const player = this.player()
if (player != null && player.setImage != null) {
player.setImage('/static/index/logo.png', '09')
}
},
setDynamicText() {
const player = this.player()
if (player == null) return
if (player.setTextWithStyle != null) {
player.setTextWithStyle('c-svga-x', '08', '#ffffff', 48, '700', '#66000000', 4, 0, 2)
return
}
if (player.setText != null) player.setText('c-svga-x', '08')
},
clearDynamicObjects() {
const player = this.player()
if (player != null && player.clearDynamicObjects != null) player.clearDynamicObjects()
},
onLoaded() {
console.log('加载完成')
},
onFinished() {
console.log('播放结束')
},
onFrame(event) {
const frame = this.readProgressValue(event, 'frame')
console.log('当前帧', frame)
},
onPercentage(event) {
const percentage = this.readProgressValue(event, 'percentage')
console.log('播放进度', percentage)
},
onError() {
console.log('加载失败')
},
readProgressValue(event, key) {
if (typeof event === 'number') return event
if (event == null) return 0
// App-Android 端通常是 Map,App-iOS 端通常是 UniEvent/detail。
if (typeof event.get === 'function') {
const value = event.get(key)
if (value != null) return value
const detail = event.get('detail')
if (detail != null) return this.readProgressValue(detail, key)
}
if (typeof event === 'object') {
if (event[key] != null) return event[key]
if (event.detail != null) return this.readProgressValue(event.detail, key)
if (event.value != null) return event.value
}
return 0
}
}
}
</script>
<style>
.page {
width: 100vw;
overflow-x: hidden;
}
.svga-box {
width: 750rpx;
height: 750rpx;
}
.content {
padding: 20rpx;
font-size: 28rpx;
}
.title {
margin-top: 20rpx;
color: #333;
}
.btnBox {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
margin-top: 20rpx;
margin-bottom: 30rpx;
}
</style>
二、Props
| 字段 |
类型 |
必填 |
默认值 |
描述 |
平台差异说明 |
| src |
String |
是 |
|
svga文件地址,支持网络地址和本地路径 |
全平台支持 |
| autoPlay |
Boolean |
否 |
true |
是否加载完成后自动播放 |
全平台支持 |
| loops |
Number |
否 |
0 |
动画循环次数,0 表示无限循环 |
全平台支持 |
| clearsAfterStop |
Boolean |
否 |
true |
动画停止时是否清空画面 |
全平台支持 |
| fillMode |
String |
否 |
Forward |
可选值 Forward / Backward;当 clearsAfterStop 为 false 时,Forward 停留最后一帧,Backward 停留第一帧 |
全平台支持 |
| isOnChange |
Boolean |
否 |
false |
是否触发 frame / percentage 进度监听 |
全平台支持 |
| canvasId |
String |
否 |
自动生成 |
canvas / Web 容器 id;同页多个组件时可手动指定,避免 id 冲突 |
仅 H5/Web、微信小程序端支持;App 端 UTS 原生组件不需要 |
| width |
String |
否 |
750rpx |
组件宽度 |
仅 H5/Web、微信小程序端作为 props 生效;App 端请通过 class / style 设置尺寸 |
| height |
String |
否 |
750rpx |
组件高度 |
仅 H5/Web、微信小程序端作为 props 生效;App 端请通过 class / style 设置尺寸 |
| debug |
Boolean |
否 |
false |
是否打印调试日志 |
当前主要用于微信小程序端音频调度排查 |
| mpAudioOffset |
Number |
否 |
0.08 |
微信小程序端 SVGA 内嵌音频补偿时间,单位秒;正数表示音频相对画面提前一点启动 |
仅微信小程序端生效 |
| mpAudioStartTimeout |
Number |
否 |
500 |
微信小程序端等待首个音频启动的最长时间,单位毫秒;避免音频准备慢时长期阻塞动画 |
仅微信小程序端生效 |
三、Event
| 字段 |
描述 |
| loaded |
监听svga文件加载完成 |
| finished |
监听动画停止播放,loops != 0 时更常用 |
| frame |
监听动画播放至某帧,isOnChange=true 时生效;事件数据中包含 frame / percentage |
| percentage |
监听动画播放至某进度,isOnChange=true 时生效;事件数据中包含 frame / percentage |
| error |
监听svga文件加载或解析失败 |
| pause |
监听动画暂停;主要由 App 原生端回调 |
| repeat |
Android 端循环回调 |
进度事件参数
App-Android / App-iOS 的 UTS 原生组件事件参数存在平台差异,建议统一用兼容方法读取:
readProgressValue(event, key) {
if (typeof event === 'number') return event
if (event == null) return 0
if (typeof event.get === 'function') {
const value = event.get(key)
if (value != null) return value
const detail = event.get('detail')
if (detail != null) return this.readProgressValue(detail, key)
}
if (typeof event === 'object') {
if (event[key] != null) return event[key]
if (event.detail != null) return this.readProgressValue(event.detail, key)
if (event.value != null) return event.value
}
return 0
}
onFrame(event) {
const frame = this.readProgressValue(event, 'frame')
}
onPercentage(event) {
const percentage = this.readProgressValue(event, 'percentage')
}
四、Methods
通用方法
this.$refs.cSvgaXRef.play()
// or
this.$refs.cSvgaXRef.pause()
| 方法 |
描述 |
| play() / start() |
开始播放动画 |
| pause() |
暂停在当前帧 |
| stop() |
停止播放动画,是否清屏由 clearsAfterStop 决定 |
| clear() |
强制清空画面 |
| setContentMode(mode) |
设置动画拉伸模式,mode 可传 Fill / AspectFill / AspectFit |
| stepToFrame(frame, andPlay) |
跳到指定帧,如果 andPlay 为 true,则从指定帧开始播放 |
| stepToPercentage(percentage, andPlay) |
跳到指定百分比,percentage 使用 0-100 |
| setImage(imagePath, key) |
设置动态图像,key 必须为 svga 文件内真实存在的图层 key |
| setText(text, key) |
设置动态文本 |
| setTextWithStyle(text, key, color, fontSize, fontWeight, shadowColor, shadowRadius, shadowDx, shadowDy) |
设置带样式的动态文本 |
| clearDynamicObjects() |
清空所有动态图像和动态文本 |
兼容方法
App-Android / App-iOS 端都兼容更接近 c-svga / 原生 SVGAPlayer 的方法名:
| 方法 |
描述 |
| playAnimation() / startAnimation() |
从第 0 帧开始播放动画 |
| playAnimationReverse() / startAnimationReverse() |
从第 0 帧开始反向播放动画 |
| startAnimationWithRange(location, length) |
播放 [location, location + length] 区间帧动画 |
| startAnimationWithRangeReverse(location, length) |
反向播放指定区间帧动画 |
| pauseAnimation() |
暂停在当前帧 |
| stopAnimation() |
停止播放动画 |
| stopAnimationWithClear(clear) |
停止播放动画,并显式指定是否清屏 |
| stepToFrameOnly(frame) |
跳到指定帧,不自动播放 |
| stepToPercentageOnly(percentage) |
跳到指定百分比,不自动播放 |
五、注意事项
c-svga-x 支持 App-Android / App-iOS 原生播放、H5/Web 播放和微信小程序播放;iOS 最低支持版本为 iOS 12。
平台实现说明
| 平台 |
实现方式 |
说明 |
| App-Android / App-iOS |
UTS 原生组件 |
使用原生 SVGA 播放能力 |
| H5/Web |
svgaplayerweb + renderjs |
兼容常用播放控制、跳帧、动态图片、动态文本 API |
| 微信小程序 |
svgaplayer-weapp + InnerAudioContext |
画面由 svgaplayer-weapp 渲染,SVGA 内嵌音频由组件额外解析并按帧调度 |
H5/Web 端基于 svgaplayerweb + renderjs 实现,支持播放、暂停、停止、跳帧、动态图片、动态文本等常用 API;阴影等部分原生文本样式会按 Web 播放器能力降级。
H5/Web 端包含音频的 SVGA 受浏览器自动播放策略影响。首次播放建议由用户点击按钮触发;组件内部已做音频解锁和 HTML5 audio pool 扩容处理,但不同浏览器仍可能要求用户交互后才允许出声。
微信小程序端基于 svgaplayer-weapp 实现,支持播放、暂停、停止、跳帧、动态图片、动态文本等常用 API;svgaplayer-weapp 本身不播放 SVGA 内嵌音频,组件会从 SVGA 的 audios 和 images[audioKey] 中提取音频,写入小程序临时文件后通过 InnerAudioContext 按帧调度播放。
微信小程序端音频不同步时,可优先调节 mpAudioOffset。默认 0.08 表示音频比当前画面提前约 80ms 启动;如果声音慢于画面,可适当增大;如果声音快于画面,可调小或设为 0。
微信小程序端没有声音时,请确认 SVGA 文件本身包含内嵌音频、设备没有静音、微信开发者工具和真机音量正常;建议先开启 debug 查看是否打印 prepared audios、autoplay、onPlay 等日志。
微信小程序端加载本地 svga 文件时,如果构建工具不拷贝 .svga 后缀资源,可以将文件后缀改为 .svg 后再传入。
UTS 通过 ref 调用组件公开方法时,不要依赖默认参数;需要反向播放、显式停止清屏、只跳帧不播放时,请使用对应的独立方法。
iOS 原生 SVGAPlayer 没有稳定的全量反向播放入口,反向播放相关方法在 iOS 端仅做 API 兼容。
动态图片/文本的 key 必须是 svga 文件里真实存在的图层 key,否则方法会正常执行,但画面不会发生变化。同一个 key 在 svga 文件中出现多次时,会尽量保持 App-Android / App-iOS 都同步替换。
clearDynamicObjects() 会清空已设置的动态图像和动态文本,并刷新当前帧;播放中调用会尽量保持原播放状态。
本地资源建议放在项目 static 目录中,并使用 /static/xxx.svga 形式传入。
网络 svga 资源会按 URL 缓存在 App 缓存目录中,首次加载成功后,再次使用同一 URL 会优先读取本地缓存,避免重复下载;加载或解析失败时会删除对应缓存。