更新记录

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 按帧播放音频。
  • 对外提供 startpausestop 等显式播放控制方法。
  • 新增事件:loadedfinishedframepercentage

平台兼容性

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 的 audiosimages[audioKey] 中提取音频,写入小程序临时文件后通过 InnerAudioContext 按帧调度播放。

微信小程序端音频不同步时,可优先调节 mpAudioOffset。默认 0.08 表示音频比当前画面提前约 80ms 启动;如果声音慢于画面,可适当增大;如果声音快于画面,可调小或设为 0

微信小程序端没有声音时,请确认 SVGA 文件本身包含内嵌音频、设备没有静音、微信开发者工具和真机音量正常;建议先开启 debug 查看是否打印 prepared audiosautoplayonPlay 等日志。

微信小程序端加载本地 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 会优先读取本地缓存,避免重复下载;加载或解析失败时会删除对应缓存。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

none

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

none

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

none

暂无用户评论。