uview-plus 3.8.130 → 3.8.136
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +64 -0
- package/components/u-cropper/u-cropper.vue +41 -12
- package/components/u-goods-sku/u-goods-sku.vue +22 -6
- package/components/u-icon/util.js +4 -1
- package/components/u-lottie/libs/LICENSE-lottie-miniprogram +21 -0
- package/components/u-lottie/libs/LICENSE-lottie-web +21 -0
- package/components/u-lottie/libs/harmony-canvas-adapter.js +394 -0
- package/components/u-lottie/libs/lottie-miniprogram.min.js +14 -0
- package/components/u-lottie/libs/lottie_svg.min.js +6 -0
- package/components/u-lottie/libs/wx-env.js +15 -0
- package/components/u-lottie/lottie.js +9 -0
- package/components/u-lottie/props.js +35 -0
- package/components/u-lottie/u-lottie.vue +635 -0
- package/components/u-number-keyboard/u-number-keyboard.vue +10 -2
- package/components/u-toast/u-toast.vue +32 -5
- package/components/u-upload/props.js +4 -0
- package/components/u-upload/u-upload.vue +3 -1
- package/components/u-upload/upload.js +2 -1
- package/components/u-upload/utils.js +21 -12
- package/libs/function/calc.js +3 -10
- package/libs/vite/app-icon-font.js +7 -5
- package/package.json +1 -1
- package/types/comps/toast.d.ts +5 -0
- package/types/comps/upload.d.ts +2 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
if (typeof wx === 'undefined') {
|
|
2
|
+
const globalObject = typeof globalThis !== 'undefined'
|
|
3
|
+
? globalThis
|
|
4
|
+
: (typeof window !== 'undefined' ? window : null)
|
|
5
|
+
if (globalObject) {
|
|
6
|
+
globalObject.wx = {
|
|
7
|
+
getSystemInfoSync() {
|
|
8
|
+
const info = uni.getSystemInfoSync() || {}
|
|
9
|
+
return Object.assign({ pixelRatio: 1, platform: '' }, info)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { defineMixin } from '../../libs/vue'
|
|
2
|
+
import LottieDefaultProps from './lottie'
|
|
3
|
+
import { registerComponentProps } from '../../libs/config/props.js'
|
|
4
|
+
|
|
5
|
+
const defProps = registerComponentProps(LottieDefaultProps)
|
|
6
|
+
|
|
7
|
+
export const props = defineMixin({
|
|
8
|
+
props: {
|
|
9
|
+
// 动画 JSON 路径,支持 http(s);H5 / App-Vue 额外支持本地静态路径
|
|
10
|
+
filePath: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: () => defProps.lottie.filePath
|
|
13
|
+
},
|
|
14
|
+
// 直接传入 Lottie JSON 对象,优先级高于 filePath
|
|
15
|
+
animationData: {
|
|
16
|
+
type: Object,
|
|
17
|
+
default: () => defProps.lottie.animationData
|
|
18
|
+
},
|
|
19
|
+
// 内容填充模式:ScaleToFill / AspectFill / Center
|
|
20
|
+
contentModel: {
|
|
21
|
+
type: String,
|
|
22
|
+
default: () => defProps.lottie.contentModel
|
|
23
|
+
},
|
|
24
|
+
// 是否循环播放
|
|
25
|
+
loop: {
|
|
26
|
+
type: Boolean,
|
|
27
|
+
default: () => defProps.lottie.loop
|
|
28
|
+
},
|
|
29
|
+
// 数据与引擎就绪后是否自动播放
|
|
30
|
+
autoplay: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: () => defProps.lottie.autoplay
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
})
|
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view class="u-lottie">
|
|
3
|
+
<!-- #ifdef H5 || APP-PLUS -->
|
|
4
|
+
<!-- #ifndef APP-NVUE -->
|
|
5
|
+
<view
|
|
6
|
+
ref="webContainer"
|
|
7
|
+
class="u-lottie__web"
|
|
8
|
+
:style="webContentStyle"
|
|
9
|
+
></view>
|
|
10
|
+
<!-- #endif -->
|
|
11
|
+
<!-- #endif -->
|
|
12
|
+
|
|
13
|
+
<!-- #ifdef MP-WEIXIN || APP-HARMONY -->
|
|
14
|
+
<u-canvas
|
|
15
|
+
v-if="canvasVisible"
|
|
16
|
+
ref="canvasRef"
|
|
17
|
+
:canvas-id="canvasId"
|
|
18
|
+
:width="canvasWidth"
|
|
19
|
+
:height="canvasHeight"
|
|
20
|
+
bg-color="transparent"
|
|
21
|
+
@ready="onCanvasReady"
|
|
22
|
+
/>
|
|
23
|
+
<!-- #endif -->
|
|
24
|
+
</view>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script>
|
|
28
|
+
import { props } from './props'
|
|
29
|
+
import { mpMixin } from '../../libs/mixin/mpMixin'
|
|
30
|
+
import { mixin } from '../../libs/mixin/mixin'
|
|
31
|
+
|
|
32
|
+
// #ifdef APP-HARMONY
|
|
33
|
+
import './libs/wx-env.js'
|
|
34
|
+
import { createHarmonyCanvasAdapter } from './libs/harmony-canvas-adapter.js'
|
|
35
|
+
// #endif
|
|
36
|
+
|
|
37
|
+
// #ifdef MP-WEIXIN || APP-HARMONY
|
|
38
|
+
import uCanvas from '../u-canvas/u-canvas.vue'
|
|
39
|
+
import lottieMiniprogram from './libs/lottie-miniprogram.min.js'
|
|
40
|
+
// #endif
|
|
41
|
+
|
|
42
|
+
// #ifdef MP-WEIXIN || APP-HARMONY
|
|
43
|
+
let canvasEngineOwner = null
|
|
44
|
+
// #endif
|
|
45
|
+
|
|
46
|
+
// #ifdef APP-PLUS
|
|
47
|
+
// #ifndef APP-NVUE
|
|
48
|
+
import lottieWeb from './libs/lottie_svg.min.js'
|
|
49
|
+
// #endif
|
|
50
|
+
// #endif
|
|
51
|
+
|
|
52
|
+
// #ifdef H5
|
|
53
|
+
let webEnginePromise = null
|
|
54
|
+
function getWebEngine() {
|
|
55
|
+
if (!webEnginePromise) {
|
|
56
|
+
webEnginePromise = import('./libs/lottie_svg.min.js')
|
|
57
|
+
.then((module) => module.default)
|
|
58
|
+
.catch((error) => {
|
|
59
|
+
webEnginePromise = null
|
|
60
|
+
throw error
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
return webEnginePromise
|
|
64
|
+
}
|
|
65
|
+
// #endif
|
|
66
|
+
|
|
67
|
+
// #ifdef APP-PLUS
|
|
68
|
+
// #ifndef APP-NVUE
|
|
69
|
+
function getWebEngine() {
|
|
70
|
+
return Promise.resolve(lottieWeb)
|
|
71
|
+
}
|
|
72
|
+
// #endif
|
|
73
|
+
// #endif
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* lottie 动画
|
|
77
|
+
* @description 加载 AE 导出的 Lottie JSON 并播放;H5/App-Vue 使用 SVG,微信小程序/鸿蒙使用 canvas。
|
|
78
|
+
* @property {String} filePath 动画 JSON 路径,支持 http(s);H5 / App-Vue 额外支持本地静态路径。远程 JSON 可包含可执行表达式,请仅加载可信来源
|
|
79
|
+
* @property {Object} animationData 直接传入 Lottie JSON 对象,优先级高于 filePath
|
|
80
|
+
* @property {String} contentModel 填充模式:ScaleToFill / AspectFill / Center
|
|
81
|
+
* @property {Boolean} loop 是否循环播放
|
|
82
|
+
* @property {Boolean} autoplay 就绪后是否自动播放
|
|
83
|
+
* @event {Function} statechange 状态变化,type 为 ready/play/pause/resume/stop/complete/error;H5 / App-Vue / 鸿蒙通常为直接对象 { type, message? },微信小程序为 event.detail,统一按 event.detail || event 兼容
|
|
84
|
+
* @example <up-lottie :animation-data="data" content-model="AspectFill" @statechange="onState" />
|
|
85
|
+
*/
|
|
86
|
+
export default {
|
|
87
|
+
name: 'u-lottie',
|
|
88
|
+
mixins: [mpMixin, mixin, props],
|
|
89
|
+
components: {
|
|
90
|
+
// #ifdef MP-WEIXIN || APP-HARMONY
|
|
91
|
+
uCanvas
|
|
92
|
+
// #endif
|
|
93
|
+
},
|
|
94
|
+
emits: ['statechange'],
|
|
95
|
+
data() {
|
|
96
|
+
return {
|
|
97
|
+
webCenterSize: null,
|
|
98
|
+
canvasVisible: false,
|
|
99
|
+
canvasWidth: 300,
|
|
100
|
+
canvasHeight: 300,
|
|
101
|
+
canvasId: `u-lottie-canvas-${Date.now()}${Math.floor(Math.random() * 1000)}`
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
computed: {
|
|
105
|
+
rendererKind() {
|
|
106
|
+
// #ifdef APP-NVUE
|
|
107
|
+
return 'unsupported'
|
|
108
|
+
// #endif
|
|
109
|
+
// #ifdef MP-WEIXIN || APP-HARMONY
|
|
110
|
+
return 'canvas'
|
|
111
|
+
// #endif
|
|
112
|
+
// #ifdef MP
|
|
113
|
+
return 'unsupported'
|
|
114
|
+
// #endif
|
|
115
|
+
// #ifdef H5 || APP-PLUS
|
|
116
|
+
return 'web'
|
|
117
|
+
// #endif
|
|
118
|
+
return 'unsupported'
|
|
119
|
+
},
|
|
120
|
+
preserveAspectRatio() {
|
|
121
|
+
if (this.contentModel === 'ScaleToFill') return 'none'
|
|
122
|
+
if (this.contentModel === 'Center') return 'xMidYMid meet'
|
|
123
|
+
return 'xMidYMid slice'
|
|
124
|
+
},
|
|
125
|
+
webContentStyle() {
|
|
126
|
+
if (this.contentModel !== 'Center') {
|
|
127
|
+
return { width: '100%', height: '100%', flex: '0 0 auto' }
|
|
128
|
+
}
|
|
129
|
+
if (this.webCenterSize) {
|
|
130
|
+
return {
|
|
131
|
+
width: `${this.webCenterSize.width}px`,
|
|
132
|
+
height: `${this.webCenterSize.height}px`,
|
|
133
|
+
flex: '0 0 auto'
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { width: '100%', height: '100%', flex: '0 0 auto' }
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
watch: {
|
|
140
|
+
filePath() {
|
|
141
|
+
this.reload()
|
|
142
|
+
},
|
|
143
|
+
animationData() {
|
|
144
|
+
this.reload()
|
|
145
|
+
},
|
|
146
|
+
contentModel() {
|
|
147
|
+
this.reload()
|
|
148
|
+
},
|
|
149
|
+
loop(value) {
|
|
150
|
+
if (this._animation) {
|
|
151
|
+
this._animation.loop = value
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
created() {
|
|
156
|
+
this._animation = null
|
|
157
|
+
this._requestTask = null
|
|
158
|
+
this._loadToken = 0
|
|
159
|
+
this._pendingPlay = false
|
|
160
|
+
this._ready = false
|
|
161
|
+
this._completed = false
|
|
162
|
+
this._engineState = 'idle'
|
|
163
|
+
this._failedToken = -1
|
|
164
|
+
this._sourceData = null
|
|
165
|
+
this._ownsCanvasEngine = false
|
|
166
|
+
this._cancelledBeforeReady = false
|
|
167
|
+
this._instanceId = `u-lottie-${Date.now()}${Math.floor(Math.random() * 1000)}`
|
|
168
|
+
this._harmonyAdapter = null
|
|
169
|
+
},
|
|
170
|
+
mounted() {
|
|
171
|
+
this.reload()
|
|
172
|
+
},
|
|
173
|
+
beforeUnmount() {
|
|
174
|
+
this._loadToken += 1
|
|
175
|
+
if (this._requestTask && typeof this._requestTask.abort === 'function') {
|
|
176
|
+
this._requestTask.abort()
|
|
177
|
+
}
|
|
178
|
+
this._requestTask = null
|
|
179
|
+
this.destroyEngine()
|
|
180
|
+
},
|
|
181
|
+
methods: {
|
|
182
|
+
async reload() {
|
|
183
|
+
const token = this._loadToken + 1
|
|
184
|
+
this._loadToken = token
|
|
185
|
+
if (this._requestTask && typeof this._requestTask.abort === 'function') {
|
|
186
|
+
this._requestTask.abort()
|
|
187
|
+
}
|
|
188
|
+
this._requestTask = null
|
|
189
|
+
this.destroyEngine()
|
|
190
|
+
this.canvasVisible = false
|
|
191
|
+
this._sourceData = null
|
|
192
|
+
this.webCenterSize = null
|
|
193
|
+
this._ready = false
|
|
194
|
+
this._completed = false
|
|
195
|
+
this._pendingPlay = false
|
|
196
|
+
this._cancelledBeforeReady = false
|
|
197
|
+
this._failedToken = -1
|
|
198
|
+
this._engineState = 'idle'
|
|
199
|
+
|
|
200
|
+
const kind = this.rendererKind
|
|
201
|
+
if (kind === 'unsupported') {
|
|
202
|
+
this.fail('当前平台不支持 u-lottie', token)
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const source = await this.resolveSource()
|
|
208
|
+
if (token !== this._loadToken) return
|
|
209
|
+
if (kind === 'web') {
|
|
210
|
+
await this.initWeb(source, token)
|
|
211
|
+
return
|
|
212
|
+
}
|
|
213
|
+
await this.initCanvasLayout(source, token)
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (token !== this._loadToken) return
|
|
216
|
+
this.fail(this.normalizeError(error), token)
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
async resolveSource() {
|
|
220
|
+
if (this.animationData && typeof this.animationData === 'object') {
|
|
221
|
+
return { data: this.animationData }
|
|
222
|
+
}
|
|
223
|
+
const path = typeof this.filePath === 'string' ? this.filePath.trim() : ''
|
|
224
|
+
if (!path) {
|
|
225
|
+
throw new Error('u-lottie: filePath 与 animationData 不能同时为空')
|
|
226
|
+
}
|
|
227
|
+
if (/^https?:\/\//i.test(path)) {
|
|
228
|
+
return { data: await this.requestJson(path) }
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// #ifdef APP-HARMONY
|
|
232
|
+
try {
|
|
233
|
+
return { data: await this.requestJson(path) }
|
|
234
|
+
} catch (error) {
|
|
235
|
+
throw new Error('u-lottie: 鸿蒙暂不支持读取本地 JSON,请改用 animationData')
|
|
236
|
+
}
|
|
237
|
+
// #endif
|
|
238
|
+
|
|
239
|
+
// #ifdef H5 || APP-PLUS
|
|
240
|
+
// #ifndef APP-NVUE
|
|
241
|
+
return { path }
|
|
242
|
+
// #endif
|
|
243
|
+
// #endif
|
|
244
|
+
|
|
245
|
+
// #ifdef MP-WEIXIN
|
|
246
|
+
throw new Error('u-lottie: 微信小程序无法读取本地 JSON,请改用 animationData')
|
|
247
|
+
// #endif
|
|
248
|
+
|
|
249
|
+
throw new Error('u-lottie: 当前平台不支持该数据源')
|
|
250
|
+
},
|
|
251
|
+
requestJson(url) {
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
const task = uni.request({
|
|
254
|
+
url,
|
|
255
|
+
method: 'GET',
|
|
256
|
+
dataType: 'json',
|
|
257
|
+
responseType: 'text',
|
|
258
|
+
success: (response) => {
|
|
259
|
+
if (this._requestTask === task) this._requestTask = null
|
|
260
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
261
|
+
reject(new Error(`u-lottie: 请求失败 (${response.statusCode})`))
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
let data = response.data
|
|
265
|
+
if (typeof data === 'string') {
|
|
266
|
+
try {
|
|
267
|
+
data = JSON.parse(data)
|
|
268
|
+
} catch (error) {
|
|
269
|
+
reject(new Error('u-lottie: JSON 解析失败'))
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (!data || typeof data !== 'object') {
|
|
274
|
+
reject(new Error('u-lottie: 返回内容不是有效的 Lottie JSON'))
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
resolve(data)
|
|
278
|
+
},
|
|
279
|
+
fail: (error) => {
|
|
280
|
+
if (this._requestTask === task) this._requestTask = null
|
|
281
|
+
const suffix = error && error.errMsg ? ` - ${error.errMsg}` : ''
|
|
282
|
+
reject(new Error(`u-lottie: 请求失败${suffix}`))
|
|
283
|
+
}
|
|
284
|
+
})
|
|
285
|
+
this._requestTask = task
|
|
286
|
+
})
|
|
287
|
+
},
|
|
288
|
+
async initWeb(source, token) {
|
|
289
|
+
// #ifdef H5 || APP-PLUS
|
|
290
|
+
// #ifndef APP-NVUE
|
|
291
|
+
let engine
|
|
292
|
+
try {
|
|
293
|
+
engine = await getWebEngine()
|
|
294
|
+
} catch (error) {
|
|
295
|
+
this.fail(this.normalizeError(error), token)
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
if (token !== this._loadToken) return
|
|
299
|
+
if (!engine || typeof engine.loadAnimation !== 'function') {
|
|
300
|
+
this.fail('lottie-web 引擎未加载', token)
|
|
301
|
+
return
|
|
302
|
+
}
|
|
303
|
+
if (source.data && this.contentModel === 'Center') {
|
|
304
|
+
this.applyWebCenterSize(source.data)
|
|
305
|
+
}
|
|
306
|
+
await this.$nextTick()
|
|
307
|
+
if (token !== this._loadToken) return
|
|
308
|
+
const containerRef = this.$refs.webContainer
|
|
309
|
+
const container = containerRef && (containerRef.$el || containerRef)
|
|
310
|
+
if (!container) {
|
|
311
|
+
this.fail('动画容器不存在', token)
|
|
312
|
+
return
|
|
313
|
+
}
|
|
314
|
+
const options = {
|
|
315
|
+
container,
|
|
316
|
+
renderer: 'svg',
|
|
317
|
+
loop: this.loop,
|
|
318
|
+
autoplay: false,
|
|
319
|
+
rendererSettings: {
|
|
320
|
+
preserveAspectRatio: this.preserveAspectRatio
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (source.data) {
|
|
324
|
+
options.animationData = source.data
|
|
325
|
+
} else {
|
|
326
|
+
options.path = source.path
|
|
327
|
+
}
|
|
328
|
+
let animation
|
|
329
|
+
try {
|
|
330
|
+
animation = engine.loadAnimation(options)
|
|
331
|
+
} catch (error) {
|
|
332
|
+
this.fail(this.normalizeError(error), token)
|
|
333
|
+
return
|
|
334
|
+
}
|
|
335
|
+
if (token !== this._loadToken) {
|
|
336
|
+
animation.destroy()
|
|
337
|
+
return
|
|
338
|
+
}
|
|
339
|
+
this._animation = animation
|
|
340
|
+
this.bindEngineEvents(animation, token)
|
|
341
|
+
// #endif
|
|
342
|
+
// #endif
|
|
343
|
+
},
|
|
344
|
+
applyWebCenterSize(data) {
|
|
345
|
+
const width = Number(data && data.w)
|
|
346
|
+
const height = Number(data && data.h)
|
|
347
|
+
if (width > 0 && height > 0) {
|
|
348
|
+
this.webCenterSize = { width, height }
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
async initCanvasLayout(source, token) {
|
|
352
|
+
if (!source.data) {
|
|
353
|
+
this.fail('画布平台仅支持 animationData 或 http(s) 数据源', token)
|
|
354
|
+
return
|
|
355
|
+
}
|
|
356
|
+
this._sourceData = source.data
|
|
357
|
+
const rect = await this.measureRoot()
|
|
358
|
+
if (token !== this._loadToken) return
|
|
359
|
+
if (!rect) {
|
|
360
|
+
this.fail('组件宽高为 0,请为 u-lottie 设置明确的宽高', token)
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
let width = rect.width
|
|
364
|
+
let height = rect.height
|
|
365
|
+
if (this.contentModel === 'Center') {
|
|
366
|
+
const naturalWidth = Number(source.data.w)
|
|
367
|
+
const naturalHeight = Number(source.data.h)
|
|
368
|
+
if (naturalWidth > 0 && naturalHeight > 0) {
|
|
369
|
+
width = naturalWidth
|
|
370
|
+
height = naturalHeight
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
this.canvasWidth = width
|
|
374
|
+
this.canvasHeight = height
|
|
375
|
+
this.canvasId = `u-lottie-canvas-${Date.now()}${Math.floor(Math.random() * 1000)}`
|
|
376
|
+
this.canvasVisible = true
|
|
377
|
+
},
|
|
378
|
+
async measureRoot() {
|
|
379
|
+
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
380
|
+
const rect = await new Promise((resolve) => {
|
|
381
|
+
uni.createSelectorQuery()
|
|
382
|
+
.in(this)
|
|
383
|
+
.select('.u-lottie')
|
|
384
|
+
.boundingClientRect((result) => resolve(result))
|
|
385
|
+
.exec()
|
|
386
|
+
})
|
|
387
|
+
if (rect && rect.width > 0 && rect.height > 0) {
|
|
388
|
+
return rect
|
|
389
|
+
}
|
|
390
|
+
await new Promise((resolve) => setTimeout(resolve, 30))
|
|
391
|
+
}
|
|
392
|
+
return null
|
|
393
|
+
},
|
|
394
|
+
onCanvasReady() {
|
|
395
|
+
if (!this.canvasVisible) return
|
|
396
|
+
const canvasRef = this.$refs.canvasRef
|
|
397
|
+
if (!canvasRef || canvasRef.canvasId !== this.canvasId) return
|
|
398
|
+
const token = this._loadToken
|
|
399
|
+
this.$nextTick(() => {
|
|
400
|
+
if (!this.canvasVisible || token !== this._loadToken) return
|
|
401
|
+
this.initCanvasEngine(token)
|
|
402
|
+
})
|
|
403
|
+
},
|
|
404
|
+
initCanvasEngine(token) {
|
|
405
|
+
// #ifdef MP-WEIXIN || APP-HARMONY
|
|
406
|
+
if (token !== this._loadToken) return
|
|
407
|
+
if (this._animation) {
|
|
408
|
+
this.destroyEngine()
|
|
409
|
+
}
|
|
410
|
+
const canvasRef = this.$refs.canvasRef
|
|
411
|
+
if (!canvasRef) {
|
|
412
|
+
this.fail('画布未就绪', token)
|
|
413
|
+
return
|
|
414
|
+
}
|
|
415
|
+
if (canvasEngineOwner && canvasEngineOwner !== this._instanceId) {
|
|
416
|
+
this.fail('当前应用仅支持一个并行的 u-lottie 实例', token)
|
|
417
|
+
return
|
|
418
|
+
}
|
|
419
|
+
if (!canvasEngineOwner) {
|
|
420
|
+
canvasEngineOwner = this._instanceId
|
|
421
|
+
this._ownsCanvasEngine = true
|
|
422
|
+
}
|
|
423
|
+
try {
|
|
424
|
+
// #ifdef MP-WEIXIN
|
|
425
|
+
const node = canvasRef.getCanvasElement()
|
|
426
|
+
const context = canvasRef.getRawContext()
|
|
427
|
+
if (!node || !context) {
|
|
428
|
+
throw new Error('画布节点初始化失败')
|
|
429
|
+
}
|
|
430
|
+
if (typeof context.setTransform === 'function') {
|
|
431
|
+
context.setTransform(1, 0, 0, 1, 0, 0)
|
|
432
|
+
}
|
|
433
|
+
lottieMiniprogram.setup(node)
|
|
434
|
+
this.startCanvasAnimation(context, token)
|
|
435
|
+
// #endif
|
|
436
|
+
|
|
437
|
+
// #ifdef APP-HARMONY
|
|
438
|
+
const legacyContext = canvasRef.getRawContext()
|
|
439
|
+
if (!legacyContext) {
|
|
440
|
+
throw new Error('画布上下文初始化失败')
|
|
441
|
+
}
|
|
442
|
+
const adapter = createHarmonyCanvasAdapter({
|
|
443
|
+
context: legacyContext,
|
|
444
|
+
width: this.canvasWidth,
|
|
445
|
+
height: this.canvasHeight
|
|
446
|
+
})
|
|
447
|
+
this._harmonyAdapter = adapter
|
|
448
|
+
lottieMiniprogram.setup(adapter.canvas)
|
|
449
|
+
this.startCanvasAnimation(adapter.context, token)
|
|
450
|
+
// #endif
|
|
451
|
+
} catch (error) {
|
|
452
|
+
// #ifdef APP-HARMONY
|
|
453
|
+
if (this._harmonyAdapter) {
|
|
454
|
+
this._harmonyAdapter.destroy()
|
|
455
|
+
this._harmonyAdapter = null
|
|
456
|
+
}
|
|
457
|
+
// #endif
|
|
458
|
+
this.releaseCanvasOwner()
|
|
459
|
+
this.fail(this.normalizeError(error), token)
|
|
460
|
+
}
|
|
461
|
+
// #endif
|
|
462
|
+
},
|
|
463
|
+
startCanvasAnimation(context, token) {
|
|
464
|
+
// #ifdef MP-WEIXIN || APP-HARMONY
|
|
465
|
+
const animation = lottieMiniprogram.loadAnimation({
|
|
466
|
+
animationData: this._sourceData,
|
|
467
|
+
loop: this.loop,
|
|
468
|
+
autoplay: false,
|
|
469
|
+
rendererSettings: {
|
|
470
|
+
context,
|
|
471
|
+
preserveAspectRatio: this.preserveAspectRatio,
|
|
472
|
+
dpr: 1,
|
|
473
|
+
clearCanvas: true
|
|
474
|
+
}
|
|
475
|
+
})
|
|
476
|
+
if (token !== this._loadToken) {
|
|
477
|
+
animation.destroy()
|
|
478
|
+
this.releaseCanvasOwner()
|
|
479
|
+
return
|
|
480
|
+
}
|
|
481
|
+
this._animation = animation
|
|
482
|
+
this.bindEngineEvents(animation, token)
|
|
483
|
+
// #endif
|
|
484
|
+
},
|
|
485
|
+
releaseCanvasOwner() {
|
|
486
|
+
// #ifdef MP-WEIXIN || APP-HARMONY
|
|
487
|
+
if (this._ownsCanvasEngine && canvasEngineOwner === this._instanceId) {
|
|
488
|
+
canvasEngineOwner = null
|
|
489
|
+
}
|
|
490
|
+
this._ownsCanvasEngine = false
|
|
491
|
+
// #endif
|
|
492
|
+
},
|
|
493
|
+
bindEngineEvents(animation, token) {
|
|
494
|
+
const isCurrent = () => token === this._loadToken
|
|
495
|
+
animation.addEventListener('DOMLoaded', () => {
|
|
496
|
+
if (!isCurrent()) return
|
|
497
|
+
if (
|
|
498
|
+
this.rendererKind === 'web' &&
|
|
499
|
+
this.contentModel === 'Center' &&
|
|
500
|
+
!this.webCenterSize
|
|
501
|
+
) {
|
|
502
|
+
this.applyWebCenterSize(animation.animationData)
|
|
503
|
+
if (this.webCenterSize) {
|
|
504
|
+
this.$nextTick(() => {
|
|
505
|
+
if (isCurrent() && this._animation) {
|
|
506
|
+
this._animation.resize()
|
|
507
|
+
}
|
|
508
|
+
})
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
this._ready = true
|
|
512
|
+
this.emitState('ready')
|
|
513
|
+
if ((this.autoplay && !this._cancelledBeforeReady) || this._pendingPlay) {
|
|
514
|
+
this._pendingPlay = false
|
|
515
|
+
this.startPlayback()
|
|
516
|
+
}
|
|
517
|
+
})
|
|
518
|
+
animation.addEventListener('complete', () => {
|
|
519
|
+
if (!isCurrent() || this.loop) return
|
|
520
|
+
this._completed = true
|
|
521
|
+
this._engineState = 'completed'
|
|
522
|
+
this.emitState('complete')
|
|
523
|
+
})
|
|
524
|
+
const onFailure = () => {
|
|
525
|
+
if (!isCurrent()) return
|
|
526
|
+
this.fail('动画加载失败', token)
|
|
527
|
+
}
|
|
528
|
+
animation.addEventListener('data_failed', onFailure)
|
|
529
|
+
animation.addEventListener('error', onFailure)
|
|
530
|
+
},
|
|
531
|
+
startPlayback() {
|
|
532
|
+
const animation = this._animation
|
|
533
|
+
if (!animation) {
|
|
534
|
+
this._pendingPlay = true
|
|
535
|
+
return
|
|
536
|
+
}
|
|
537
|
+
if (this._engineState === 'playing') return
|
|
538
|
+
if (this._completed || this._engineState === 'stopped') {
|
|
539
|
+
animation.goToAndStop(0, true)
|
|
540
|
+
this._completed = false
|
|
541
|
+
}
|
|
542
|
+
animation.play()
|
|
543
|
+
this._engineState = 'playing'
|
|
544
|
+
this.emitState('play')
|
|
545
|
+
},
|
|
546
|
+
playAnimation() {
|
|
547
|
+
this._cancelledBeforeReady = false
|
|
548
|
+
if (!this._animation || !this._ready) {
|
|
549
|
+
this._pendingPlay = true
|
|
550
|
+
return
|
|
551
|
+
}
|
|
552
|
+
this.startPlayback()
|
|
553
|
+
},
|
|
554
|
+
pauseAnimation() {
|
|
555
|
+
if (!this._animation || this._engineState !== 'playing') return
|
|
556
|
+
this._animation.pause()
|
|
557
|
+
this._engineState = 'paused'
|
|
558
|
+
this.emitState('pause')
|
|
559
|
+
},
|
|
560
|
+
resumeAnimation() {
|
|
561
|
+
if (!this._animation || this._engineState !== 'paused') return
|
|
562
|
+
this._animation.play()
|
|
563
|
+
this._engineState = 'playing'
|
|
564
|
+
this.emitState('resume')
|
|
565
|
+
},
|
|
566
|
+
cancelAnimation() {
|
|
567
|
+
this._cancelledBeforeReady = true
|
|
568
|
+
this._pendingPlay = false
|
|
569
|
+
if (!this._animation) return
|
|
570
|
+
this._animation.stop()
|
|
571
|
+
this._completed = false
|
|
572
|
+
this._engineState = 'stopped'
|
|
573
|
+
this.emitState('stop')
|
|
574
|
+
},
|
|
575
|
+
emitState(type, message = '') {
|
|
576
|
+
const detail = { type }
|
|
577
|
+
if (type === 'error') {
|
|
578
|
+
detail.message = message
|
|
579
|
+
}
|
|
580
|
+
this.$emit('statechange', detail)
|
|
581
|
+
},
|
|
582
|
+
fail(message, token) {
|
|
583
|
+
if (typeof token === 'number' && token !== this._loadToken) return
|
|
584
|
+
if (this._failedToken === this._loadToken) return
|
|
585
|
+
this._failedToken = this._loadToken
|
|
586
|
+
this._ready = false
|
|
587
|
+
this._pendingPlay = false
|
|
588
|
+
this._completed = false
|
|
589
|
+
this.destroyEngine()
|
|
590
|
+
this._engineState = 'error'
|
|
591
|
+
this.emitState('error', message || '动画初始化失败')
|
|
592
|
+
},
|
|
593
|
+
normalizeError(error) {
|
|
594
|
+
if (!error) return '动画初始化失败'
|
|
595
|
+
if (typeof error === 'string') return error
|
|
596
|
+
return error.message || error.errMsg || '动画初始化失败'
|
|
597
|
+
},
|
|
598
|
+
destroyEngine() {
|
|
599
|
+
if (this._animation) {
|
|
600
|
+
try {
|
|
601
|
+
this._animation.destroy()
|
|
602
|
+
} catch (error) {
|
|
603
|
+
// 引擎可能尚未完成初始化,销毁失败不影响后续重载
|
|
604
|
+
}
|
|
605
|
+
this._animation = null
|
|
606
|
+
}
|
|
607
|
+
// #ifdef APP-HARMONY
|
|
608
|
+
if (this._harmonyAdapter) {
|
|
609
|
+
this._harmonyAdapter.destroy()
|
|
610
|
+
this._harmonyAdapter = null
|
|
611
|
+
}
|
|
612
|
+
// #endif
|
|
613
|
+
this.releaseCanvasOwner()
|
|
614
|
+
this._ready = false
|
|
615
|
+
this._engineState = 'idle'
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
</script>
|
|
620
|
+
|
|
621
|
+
<style lang="scss" scoped>
|
|
622
|
+
.u-lottie {
|
|
623
|
+
position: relative;
|
|
624
|
+
display: flex;
|
|
625
|
+
align-items: center;
|
|
626
|
+
justify-content: center;
|
|
627
|
+
width: 100%;
|
|
628
|
+
height: 100%;
|
|
629
|
+
overflow: hidden;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.u-lottie__web {
|
|
633
|
+
flex: 0 0 auto;
|
|
634
|
+
}
|
|
635
|
+
</style>
|