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.
@@ -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,9 @@
1
+ export default {
2
+ lottie: {
3
+ filePath: '',
4
+ animationData: null,
5
+ contentModel: 'AspectFill',
6
+ loop: false,
7
+ autoplay: true
8
+ }
9
+ }
@@ -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>