更新记录
1.3.0(2026-10-20)
- 修复 Android 云打包
compileReleaseKotlin编译失败(wb-svga-player compile error):- UTS 侧
this.$el编译产物是FrameLayout?,而 Kotlin 桥形参写的是非空View, 严格签名匹配下报Argument type mismatch: FrameLayout? / View。 现把桥上所有容器形参统一为View?,null 时静默 no-op(不再依赖 UTS 侧逐个!断言)。 - UTS 的
number编译成 KotlinNumber,桥里形参写Int/Double会报actual type is 'Number', but 'Int'/'Double' was expected。 现数字形参(loops/volume/fontSize/percentage/frame/startPct/endPct) 统一用Number接收,函数体内toInt()/toDouble()/toFloat()转换。 - 行为与 API 无变化,页面代码无需改动。
- UTS 侧
- 修复 iOS 云打包(两个坑连着踩,最终形态:内置预编译静态库):
- 坑 1
OSAtomicCompareAndSwapPtrBarrier:SVGAPlayer 2.5.7里的 protoc 生成码用了 OSAtomic, Xcode 16+ / iOS 18+ SDK 把该系列挪进Darwin.libkern.OSAtomicDeprecated模块, 不显式 import 就报call to undeclared function/must be imported from module/conflicting types; 报错在 pod 自己的 target 上,插件侧改配置无效,云端也不允许自定义 Podfile。 - 坑 2
cannot find type 'SVGAPlayer' in scope:把源码放进app-ios/src/后,.m能编进去, 但.h不会写进模块伞头文件(HBuilderX 的addSourceFile()追伞头文件那步先于伞头文件创建, 直接跳过),Swift 仍然看不到 ObjC 类;只有Libs/<dir>/*.h会被addLibrary()写进伞头文件。 - 最终处理:
SVGAPlayer 2.5.7(Apache-2.0)打 3 处补丁后预编译为utssdk/app-ios/Libs/SVGAPlayer/libSVGAPlayer.a+ 15 个公开头文件;补丁与重建命令见Libs/SVGAPlayer/HOW-TO-BUILD.md(GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS=1、#import <libkern/OSAtomic.h>、[worker release]改 ARC 安全)。 config.json的 pod 改为Protobuf (~> 3.4)+SSZipArchive (>= 1.8.1)(提供 GPB / zip 符号), 补libz.tbd;SvgaViewBridge.swift不写import SVGAPlayer,类型由伞头文件带进来。- 已按 HBuilderX 的真实构建方式(伞头文件 + Public Headers + 静态库 + pod 链接)在
Xcode 26.6 / Protobuf 3.29.6 / SSZipArchive 2.4.3 下验证
BUILD SUCCEEDED; 反向验证(伞头文件去掉 import)可精确复现云端那 7 条cannot find type报错。
- 坑 1
- 修复 iOS 编译失败(
cannot convert value of type 'UTSJSONObject?' to expected argument type '[String : Any]?'): iOS 的UTSComponent.__$emit第二个参数是NSDictionary([String: Any]?),UTSJSONObject不是字典,直接传编译不过。iOS 侧改为payload.toMap()(Swift 里就是toMap() -> [String : Any],已从DCloudUTSFoundation.swiftinterface确认); Android 侧保持UTSJSONObject——toMap()在 uni-app(非 x)的 Android 上不可用,两端写法必须分开。 - 修复 Android 编译失败(
Unresolved reference 'FrameLayout'/Assignment type mismatch):SvgaViewBridge.kt新增SvgaContainer : FrameLayout时漏了import android.widget.FrameLayout和import android.view.View.MeasureSpec,补齐;SVGAVideoEntity.videoSize是SVGARect,宽高都是 Double,赋给aspectRatio: Float需要toFloat();SvgaController.setScaleMode与var scaleMode生成的 setter 撞 JVM 签名,改名applyScaleMode(与setLoops/setVolume/setInteractive同一类问题)。- 已用
kotlinc+android-34.jar+ SVGAPlayer 2.6.1classes.jar真实编译通过(0 error)。
- 新增
scaleType属性(默认fitCenter):centerCrop= 铺满容器、超出裁切(直播 / 抖音全屏特效), 另有fitXY、centerInside。Android 走SVGADrawable.scaleType(同时设置 view 和 drawable), iOS 走player.contentMode;运行期可用updateScaleType(mode)切换。 - 尺寸自适应:Android 承载容器换成
SvgaContainer——样式没写死高度时按素材宽高比撑开 (宽度 ÷ (w/h)),不同尺寸的 svga 都能完整显示;写死高度则以样式为准。iOS 侧contentMode固定按比例居中。示例页主播放器高度改由loaded事件计算(fitBox),不再固定 320/360。 - 示例页新增「全屏特效」演示:
position: fixed铺满整屏 +scale-type="centerCrop"+volume=0+loops=0,附关闭按钮;并演示「遮罩挡住下层 / 点击穿透」两种模式可切换。 - 文档新增「10.3 全屏后还能点到下面的按钮?」:全屏特效默认
interactive=false是穿透的; 挡住下层要打开:interactive="true"(原生 SVGAImageView 铺满整屏并消费触摸), 透明view遮罩在 nvue 里拦不住(已废弃该方案)。另附「全屏层必须放在scroll-view外面」的 结构要求:放在滚动容器里会被裁剪、且绝对定位子节点拿不到尺寸和触摸。 - 示例页修正全屏特效结构:根节点改为
view.root,scroll-view与.fx-layer变成兄弟节点;fxBlock直接驱动:interactive,不再用遮罩;@event收到type=click即证明触摸被特效层消费。 - 文档新增「十、尺寸自适应与全屏特效(直播 / 抖音式)」。
- 修复事件载荷丢字段(页面显示
undefinedxundefined/NaN%):- 根因:事件载荷用的是 UTS
Map<string,string>,在 nvue(uni-app 非 x)上过桥到 JS 只剩第一个键, 所以e.type有值、e.width/e.frames/e.frame/e.percentage全是undefined。 - 现改为把原生 JSON 直接
JSON.parseObject成UTSJSONObject再$emit,字段与数值类型原样带到 JS; Android 的loaded事件同时补上audio(音频轨条数),两端对齐。
- 根因:事件载荷用的是 UTS
- 修复 Android 首帧按原始尺寸绘制、底部被裁:
setVideoItem新建的SVGADrawable默认scaleType = MATRIX(不缩放、左上角对齐),要等play()才同步;现在挂载后立刻强制FIT_CENTER。 - 示例页改为可滚动(根节点
<scroll-view scroll-y>):nvue 页面本身不滚动, 之前底部「静音/音频诊断/强制有声」这一行会被屏幕切掉。 - Android 可诊断性 + 防闪退护栏:
attach/load/loadAssets/applyEntity加Log.i/e(tagwb-svga), 能直接看出:容器是否就绪、解析成功与否、videoSize/frames/fps、viewSize; 资源解析失败时会把 APK assets 根目录下能找到的.svga列表一起打出来, 一眼区分"资源没打进包"和"文件本身坏了"。- 未加载成功(entity 为空)时
play/stepTo/stepToFrame/playRange直接忽略并打 warn, 不再把空对象递进 SVGAPlayer(stepToFrame内部会摸drawable.videoItem.frames)。 - 所有桥接方法体和事件回调统一用
safe()包catch (Throwable): 原生库抛异常(含基座里没有 SVGAPlayer 的NoClassDefFoundError)只记日志,不再闪退。
- 不兼容变更:4 个方法改名为
updateXxx(iOS 编译要求,Android 同步):setLoops→updateLoops、setFillMode→updateFillMode、setVolume→updateVolume、setInteractive→updateInteractive。 原因:UTS 组件编到 iOS 是@objcMembers的 Swift 类,prop 自带 ObjC settersetLoops:, 同名方法撞选择器 →method 'setLoops' with Objective-C selector 'setLoops:' conflicts with setter。setMute/setDynamicImage/setDynamicText/setHidden/setAudioSession没有同名 prop,保持不变。
平台兼容性
uni-app(5.26)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | - | - | × | √ | √ | √ | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
wb-svga-player · SVGA 动画播放器(Android / iOS)
uni-app 的 SVGA 动画组件,原生渲染(Android 走 SVGAPlayer-Android,iOS 走 SVGAPlayer), 用于直播间礼物特效、加载动画、活动弹窗、播放器贴片等场景。
特性
- 📱 Android / iOS 双端原生播放,
nvue页面使用 - ▶️ 播放 / 暂停 / 停止 / 定位到帧或百分比 / 区间播放(支持倒放)
- 🔁 循环次数、填充模式(Forward / Backward / Clear)
- 📐 填充方式(完整居中 / 铺满裁切 / 拉伸),支持按素材比例自适应高度
- 🔊 音量与静音(素材内嵌音频时生效)
- 🖼 动态替换图片、动态文字、隐藏指定图层
- 👆 点击穿透开关:盖在按钮上也不挡操作,或反过来吃掉点击
- 📣 进度 / 结束 / 循环 / 出错等事件回调
- 🎁 适合做「直播送礼全屏特效」:铺满裁切 + 触摸穿透 + 队列连播
一、平台支持
| 平台 | 支持 | 说明 |
|---|---|---|
| App-Android | ✅ | 依赖 SVGAPlayer-Android 2.6.1(JitPack,构建时自动拉取) |
| App-iOS | ✅ | 已内置 SVGAPlayer 2.5.7 预编译静态库,无需 CocoaPods 额外配置 |
| H5 / 小程序 / uni-app x | ❌ | 仅 App 端 · 仅 nvue 页面 |
使用前提
- HBuilderX ≥ 3.7.0(
uni_modules+ UTS 插件); - 必须使用自定义基座:组件含原生代码,标准基座会提示找不到 SVGAPlayer;
- 页面后缀必须是
.nvue:原生组件只能放在 nvue 页面里(.vue页面不显示); - 组件需要明确的宽高,见「八、尺寸与填充」。
二、安装
- 在插件市场点「下载插件并导入 HBuilderX」,选择你的项目;
- 确认项目里出现目录:
uni_modules/wb-svga-player/; - 不需要 npm 安装、不需要手动配 Podfile;
- 首次使用:发行 → 制作自定义基座 → 运行。
升级插件后如果行为没变化,删掉
unpackage/dist/build再打包一次,避免复用旧产物。
三、3 分钟上手
新建 pages/demo/demo.nvue:
<template>
<view class="page">
<view class="box">
<wb-svga-player
ref="svga"
class="player"
src="rose.svga"
:loops="0"
:volume="1"
@event="onEvent" />
</view>
<text class="tip">{{ tip }}</text>
</view>
</template>
<script>
export default {
data() {
return {
tip: '加载中…'
}
},
methods: {
onEvent(e) {
if (e.type == 'loaded') {
this.tip = `${e.width}x${e.height} · ${e.frames} 帧 · ${e.fps} FPS`
} else if (e.type == 'error') {
this.tip = '加载失败:' + e.message
}
}
}
}
</script>
<style>
.page {
flex: 1;
background-color: #f2f3f5;
padding: 12px;
}
/* 父容器负责尺寸,播放器绝对定位铺满 */
.box {
position: relative;
height: 320px;
background-color: #ffffff;
border-radius: 12px;
overflow: hidden;
}
.player {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.tip {
margin-top: 10px;
font-size: 13px;
color: #666666;
}
</style>
三个必须知道的点
- 页面必须是
.nvue,.vue页面里不会渲染; - 播放器要有明确宽高:父容器给
height,播放器用position: absolute; top/left/right/bottom: 0铺满。 不要只写flex: 1—— Android 上原生组件按 flex 量出来的高度可能是 0,表现是「白盒子在、动画不显示」; -
src支持三种写法:- 内置/打包进 apk 的资源名:
rose.svga - 网络地址:
https://xxx.com/a.svga -
本地文件:绝对路径或
file://开头,例如file:///data/user/0/包名/files/a.svga。 用uni.downloadFile下载后,先转成绝对路径再传:uni.downloadFile({ url: 'https://xxx.com/a.svga', success(res) { const path = plus.io.convertLocalFileSystemURL(res.tempFilePath) that.svgaSrc = path } })
- 内置/打包进 apk 的资源名:
自带三个示例素材:
rose.svga、kingset.svga、heartbeat.svga,装好即可跑通。
四、属性(props)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
src |
String | '' |
资源地址,见上「三种写法」 |
active |
Boolean | true |
是否为当前激活项。列表 / swiper 里切页时置 false 暂停、置 true 播放 |
autoPlay |
Boolean | true |
解析完成后自动播放(还需 active 为 true) |
loops |
Number | 0 |
0 = 无限循环;N = 播放 N 次(结束后回调 finished) |
fillMode |
String | Forward |
停止后的表现:Forward 停在最后一帧 / Backward 回到第一帧 / Clear 清空画布 |
scaleType |
String | fitCenter |
填充方式:fitCenter 完整居中(可能留白)/ centerCrop 铺满裁切 / fitXY 拉伸 / centerInside |
volume |
Number | 1 |
音量 0 ~ 1,0 为静音(素材内嵌音频时才生效) |
clearsAfterStop |
Boolean | false |
停止后是否清屏 |
interactive |
Boolean | false |
false 触摸穿透(推荐,装饰层不挡操作);true 吃掉点击并回调 click |
debug |
Boolean | false |
打印内部事件到控制台(progress 很密,调试完记得关) |
<wb-svga-player
src="kingset.svga"
:loops="1"
fill-mode="Backward"
scale-type="centerCrop"
:volume="0"
:interactive="false" />
五、事件(统一 @event)
载荷是一个普通对象,用 type 区分:
type |
附带字段 | 触发时机 |
|---|---|---|
loaded |
width height frames fps audio |
素材解析完成、即将开始播放(audio 为音频轨条数,0 = 无声音) |
progress |
frame percentage(0~1) |
播放过程中,每帧回调(≈ fps 次/秒) |
finished |
— | 播放结束(仅 loops > 0 时才会来,loops = 0 是无限循环) |
repeat |
— | 每循环一轮回调一次 |
pause |
— | 暂停时 |
click |
— | 仅在 interactive = true 时,点击动画区域触发 |
error |
message |
资源不存在 / 解析失败 / 播放异常 |
<wb-svga-player src="rose.svga" @event="onEvent" />
export default {
methods: {
onEvent(e) {
switch (e.type) {
case 'loaded':
// e.width / e.height / e.frames / e.fps / e.audio 都是 number
console.log('尺寸', e.width, e.height, '音频轨', e.audio)
break
case 'progress':
// 别在这里做重活,每秒会来 fps 次
break
case 'finished':
console.log('播完了')
break
case 'error':
console.log('出错:', e.message)
break
}
}
}
}
做「加载中」提示时,就用
loaded当开关:它正好在「已有第一帧、正要开播」时发出。
六、方法(this.$refs.xxx.方法())
播放控制
| 方法 | 说明 |
|---|---|
play() |
播放 / 继续 |
pause() |
暂停 |
stop() |
停止(按 fillMode 决定停在第一帧还是最后一帧) |
stopAndClear() |
停止并清空画布 |
reload() |
用当前 src 重新加载并播放(同一素材重播用这个) |
load(src) |
手动加载另一个动画(不修改 props) |
stepTo(percentage) |
定位到 0~1 的进度 |
stepToFrame(frame, andPlay) |
定位到指定帧,andPlay 为 true 时从该帧继续播 |
playRange(startPct, endPct, reverse) |
播放 0~1 区间,reverse = true 倒放 |
isAnimating() |
是否正在播放 |
运行期改参数(等价于改 props,但不触发重新加载)
| 方法 | 说明 |
|---|---|
updateLoops(n) |
改循环次数 |
updateFillMode(mode) |
改填充模式 |
updateScaleType(mode) |
改填充方式 |
updateVolume(v) |
改音量 0~1 |
updateInteractive(boolean) |
改点击穿透 |
setMute(boolean) |
静音开关(会记住静音前的音量) |
动态元素
| 方法 | 说明 |
|---|---|
setDynamicImage(key, src) |
替换素材里 key 对应的图层图片(src 支持本地/网络) |
setDynamicText(key, text, fontSize, color) |
设置动态文字 |
setHidden(key, hidden) |
隐藏 / 显示某个图层 |
clearDynamic() |
清空所有动态元素 |
信息读取
| 方法 | 说明 |
|---|---|
getWidth() / getHeight() |
素材画布宽高 |
getFrames() / getFps() |
总帧数 / 帧率 |
hasAudio() |
是否含音频轨 |
audioDebug() |
音频诊断信息(排查没声音时用) |
<wb-svga-player ref="svga" src="rose.svga" :loops="0" @event="onEvent" />
<view class="row">
<text class="btn" @click="$refs.svga.pause()">暂停</text>
<text class="btn" @click="$refs.svga.play()">继续</text>
<text class="btn" @click="$refs.svga.stepTo(0.5)">定位 50%</text>
<text class="btn" @click="$refs.svga.updateLoops(3)">循环 3 次</text>
</view>
方法名为什么是
updateXxx而不是setXxx:loops/volume/fillMode/scaleType这些属性 在原生侧自带setXxx的 setter,方法再叫setXxx会冲突,所以统一用updateXxx。
七、常用场景
7.1 固定尺寸的装饰动画
<view style="width: 120px; height: 120px; position: relative;">
<wb-svga-player class="abs" src="heartbeat.svga" scale-type="fitCenter" />
</view>
.abs { position: absolute; top: 0; left: 0; right: 0; bottom: 0; }
7.2 高度跟着素材走(不同尺寸都完整显示)
素材画布尺寸各不相同(600×600、750×420、1080×1920…),用 loaded 里的宽高比算容器高度:
<view class="box" :style="{ height: boxH + 'px' }">
<wb-svga-player class="abs" :src="src" @event="onEvent" />
</view>
data() {
return { src: 'rose.svga', boxH: 320 }
},
methods: {
onEvent(e) {
if (e.type != 'loaded') return
const boxW = uni.getSystemInfoSync().windowWidth - 24 // 页面左右各 12px padding
this.boxH = Math.max(120, Math.round(boxW * e.height / e.width))
}
}
7.3 全屏特效(直播间送礼 / 抖音那种)
铺满整屏、超出裁切、触摸穿透(用户还能继续点礼物和点赞):
<!-- 放在页面根 view 的最后,且不要在 scroll-view 里面 -->
<view v-if="fxOn" class="fx-layer">
<wb-svga-player
class="abs"
:src="fxSrc"
scale-type="centerCrop"
:interactive="false"
:loops="1"
:volume="0"
@event="onFxEvent" />
</view>
.fx-layer { position: absolute; top: 0; left: 0; right: 0; bottom: 0; }
.abs { position: absolute; top: 0; left: 0; right: 0; bottom: 0; }
三个要点:
scale-type="centerCrop"—— 全屏必须铺满裁切;用fitCenter会留黑边;interactive="false"—— 特效层不吃触摸,不挡下层按钮和滑动;- 特效层要放在
scroll-view外面(页面根view的最后一个子节点)。放在滚动容器里会被裁剪, 而且绝对定位的子节点拿不到正确的尺寸和触摸。
需要「挡住下层」(模态特效,例如抽奖中不可操作)时,把
interactive设成true即可 —— 因为组件铺满整屏,它就会吃掉整屏的点击,并回调click事件。 不要用透明view当遮罩:nvue 里它拦不住原生组件的触摸。
7.4 队列连播 + 加载态
连发多个礼物时排队播,一段播完接着下一段,中间不留黑屏。
<view v-if="fxOn" class="fx-layer">
<wb-svga-player
ref="fx"
class="abs"
:src="fxSrc"
scale-type="centerCrop"
:interactive="false"
:loops="1"
:volume="0"
@event="onFxEvent" />
<!-- 加载态:只在屏幕为空时显示 -->
<view v-if="fxLoading" class="fx-loading">
<text class="fx-loading-txt">加载中…</text>
</view>
</view>
export default {
data() {
return {
fxOn: false,
fxSrc: 'rose.svga',
fxLoading: false,
fxQueue: [],
fxTimer: null,
lastFinishAt: 0
}
},
methods: {
/** 送礼物:入队 */
sendGift(src) {
if (this.fxQueue.length < 3) this.fxQueue.push(src)
this.startIfIdle()
},
startIfIdle() {
if (this.fxOn || this.fxQueue.length == 0) return
this.fxSrc = this.fxQueue.shift()
this.fxOn = true
this.fxLoading = true // 屏幕是空的,显示加载态
this.armWatchdog()
},
/** 一段播完:队列还有就原地换素材,不卸载组件(所以无黑屏) */
onFxEvent(e) {
if (e.type == 'loaded') { // 解析完成、正要开播 → 收加载态
this.fxLoading = false
return
}
if (e.type == 'finished' || e.type == 'error') {
const now = Date.now()
if (now - this.lastFinishAt < 120) return // 去重
this.lastFinishAt = now
if (this.fxQueue.length > 0) {
const next = this.fxQueue.shift()
const same = next == this.fxSrc
this.fxSrc = next
if (same) this.$refs.fx.reload() // 同一素材重播
this.armWatchdog()
return
}
this.fxOn = false
}
},
armWatchdog() {
clearTimeout(this.fxTimer)
this.fxTimer = setTimeout(() => { // 素材异常不回调时兜底
this.onFxEvent({ type: 'finished' })
}, 12000)
}
}
}
要点:
- 全程只用一个组件实例,播完只换
src,不销毁重建 —— 上一帧会留在屏幕上,新素材解析好直接接上; - 连续送同一个礼物时
src没变,props 不会触发重新加载,要手动reload(); - 加载态只在「屏幕为空」时显示;续播时别显示,否则会出现「动效已经在播、头上还顶着加载中」。
7.5 swiper / 列表里复用
用 active 控制播停,离开屏幕的项自动暂停,不占 CPU:
<swiper :current="current" @change="onChange" style="flex: 1;">
<swiper-item v-for="(item, i) in list" :key="i">
<view class="full">
<wb-svga-player class="abs" :src="item.src" :active="i == current" :loops="0" />
</view>
</swiper-item>
</swiper>
onChange(e) {
this.current = e.detail.current
}
7.6 动态文字 / 图片 / 隐藏图层
需要素材里已经留好对应的 key(在 SVGA 制作工具里给图层命名):
const svga = this.$refs.svga
svga.setDynamicText('nickname', '小柚子', 24, '#FFD24D') // key, 文案, 字号, 颜色
svga.setDynamicImage('avatar', 'https://xxx.com/a.png') // key, 图片地址
svga.setHidden('badge', true) // 隐藏某个图层
svga.clearDynamic() // 全部还原
7.7 音量与静音
<wb-svga-player ref="svga" src="with_audio.svga" :volume="1" :loops="0" />
<text class="btn" @click="$refs.svga.setMute(true)">静音</text>
- 只有素材内嵌音频轨才会出声,可用
loaded事件的audio字段判断(0= 无声音); - Android 的音量是全局的(SVGASoundManager 单例),调小会影响同进程其他 SVGA;
- iOS 静音键 / 静音开关会影响播放,可用
setAudioSession('playback')让静音模式下也出声。
八、尺寸与填充
两条规则
- 容器必须有明确宽高(父级给
height,播放器position: absolute铺满,或直接写死宽高); 高度为 0 时什么都不显示; - 素材比例和容器比例不一致时,由
scaleType决定怎么放:
scaleType |
效果 | 适用 |
|---|---|---|
fitCenter(默认) |
完整显示、居中,可能留白 | 图标、装饰动画 |
centerCrop |
铺满容器,超出部分裁切 | 全屏特效 / 背景动画 |
fitXY |
拉伸铺满(会变形) | 极少用 |
centerInside |
素材比容器大时才缩小 | 少用 |
<!-- 完整显示 -->
<wb-svga-player src="rose.svga" scale-type="fitCenter" />
<!-- 铺满裁切 -->
<wb-svga-player src="rose.svga" scale-type="centerCrop" />
九、触摸与层级
组件是普通 View(Android ImageView / iOS UIView),不会浮到 Window 层,也不影响其它组件层级。
| 需求 | 写法 |
|---|---|
| 纯装饰,不挡点击(默认) | :interactive="false" |
| 吃掉点击(模态特效、点特效关闭) | :interactive="true",并在 @event 里判断 e.type == 'click' |
<view class="fx-layer" v-if="fxOn">
<wb-svga-player
class="abs"
src="rose.svga"
scale-type="centerCrop"
:interactive="true"
@event="onFxEvent" />
<!-- 关闭按钮要写在组件**后面**(DOM 顺序在后 = 层级在上),否则会被整屏吃掉点不到 -->
<text class="close" @click="fxOn = false">关闭</text>
</view>
想让特效下面还能操作(抖音式礼物特效),保持
interactive="false"就好, 不要额外加透明遮罩 —— nvue 里view不绑事件不吃触摸,绑了也常常拦不住原生组件。
十、常见问题
1. Android 上只有白底,动画完全不显示
容器没给播放器明确宽高。检查是否有 position: absolute; top/left/right/bottom: 0 或写死的宽高;
只写 flex: 1 在 Android 上可能量出 0 高度(iOS 反而正常,容易误判成"Android 不支持")。
2. 运行时提示找不到 SVGAPlayer / ClassNotFound
用的是标准基座。组件含原生代码,必须 制作自定义基座 后运行。
3. 事件里 e.width、e.frame 全是 undefined
旧版本在 nvue 下事件载荷会丢字段,1.3.0 已修复(改为传 JSON 对象)。升级插件并重新打包即可。
4. iOS 云打包报 OSAtomicCompareAndSwapPtrBarrier 或 cannot find type 'SVGAPlayer' in scope
1.3.0 起已内置打补丁的静态库,不再依赖 CocoaPods 拉 SVGAPlayer,正常不会出现。
若仍报错:删掉 unpackage/dist/build 后重新打包;还是不行请带上完整日志反馈。
5. 用自己的 svga 素材
三种方式,任选:
- 网络地址(推荐):上传到 CDN,
src="https://xxx.com/gift.svga"; - 本地文件:
uni.downloadFile下载后,用plus.io.convertLocalFileSystemURL(res.tempFilePath)转成绝对路径再传给src(临时路径是_doc/...相对形式,原生层认不出来); - 打进安装包:把文件放进
uni_modules/wb-svga-player/utssdk/app-android/assets/(Android) 和utssdk/app-ios/Resources/(iOS),之后用文件名引用。注意这样每次换素材都要重新打包。
6. loops 设了没反应 / 收不到 finished
loops = 0 是无限循环,永远不会结束;要播 N 次就设 loops 为 N,才会在最后回调 finished。
7. 没有声音
只有「内嵌音频轨」的 svga 才有声音(多数礼物素材是无声的)。
loaded 事件的 audio 字段为 0 就是没音频轨;hasAudio() 等价判断。
8. 同屏多个大动画卡顿
每个实例都会独立解码。列表 / 轮播里用 active 控制只让当前项播放;
尽量复用同一个实例切 src,而不是同时挂很多个。
9. iOS 模拟器链接失败
内置静态库只有 arm64 真机切片,请用真机或自定义基座运行。
十一、版本与许可
- 当前版本:1.3.0(更新记录见同目录
changelog.md) - 组件代码:Apache-2.0
- 内置播放内核(均为 Apache-2.0):
- Android:
com.github.svga:SVGAPlayer-Android:2.6.1(JitPack) - iOS:
SVGAPlayer 2.5.7预编译静态库(已修补 Xcode 16+ 的编译问题)
- Android:
- 插件不采集任何用户数据;使用网络地址时由播放器直接向该地址请求资源。

收藏人数:
购买普通授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 14
赞赏 0
下载 12698371
赞赏 1956
赞赏
京公网安备:11010802035340号