@react-native-ohos/lottie-react-native 7.2.3-rc.1 → 7.2.3-rc.3

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.
Files changed (47) hide show
  1. package/LICENSE +200 -200
  2. package/README.OpenSource +10 -10
  3. package/README.md +12 -12
  4. package/harmony/lottie/BuildProfile.ets +20 -20
  5. package/harmony/lottie/build-profile.json5 +8 -8
  6. package/harmony/lottie/hvigorfile.ts +5 -5
  7. package/harmony/lottie/index.ets +5 -5
  8. package/harmony/lottie/oh-package.json5 +14 -14
  9. package/harmony/lottie/src/main/cpp/CMakeLists.txt +9 -9
  10. package/harmony/lottie/src/main/cpp/LottieAnimationViewPackage.h +12 -12
  11. package/harmony/lottie/src/main/cpp/generated/RNOH/generated/BaseLottieReactNativePackage.h +68 -68
  12. package/harmony/lottie/src/main/cpp/generated/RNOH/generated/components/LottieAnimationViewJSIBinder.h +43 -43
  13. package/harmony/lottie/src/main/cpp/generated/react/renderer/components/lottie_react_native/ComponentDescriptors.h +22 -22
  14. package/harmony/lottie/src/main/cpp/generated/react/renderer/components/lottie_react_native/EventEmitters.cpp +44 -44
  15. package/harmony/lottie/src/main/cpp/generated/react/renderer/components/lottie_react_native/EventEmitters.h +39 -39
  16. package/harmony/lottie/src/main/cpp/generated/react/renderer/components/lottie_react_native/Props.cpp +41 -41
  17. package/harmony/lottie/src/main/cpp/generated/react/renderer/components/lottie_react_native/Props.h +42 -42
  18. package/harmony/lottie/src/main/cpp/generated/react/renderer/components/lottie_react_native/ShadowNodes.cpp +19 -19
  19. package/harmony/lottie/src/main/cpp/generated/react/renderer/components/lottie_react_native/ShadowNodes.h +34 -34
  20. package/harmony/lottie/src/main/cpp/generated/react/renderer/components/lottie_react_native/States.cpp +18 -18
  21. package/harmony/lottie/src/main/cpp/generated/react/renderer/components/lottie_react_native/States.h +35 -35
  22. package/harmony/lottie/src/main/ets/LottieAnimationTools.ets +20 -20
  23. package/harmony/lottie/src/main/ets/LottieAnimationView.ets +395 -393
  24. package/harmony/lottie/src/main/ets/LottieAnimationViewPackage.ts +16 -16
  25. package/harmony/lottie/src/main/ets/LottieCompositionCache.ets +29 -29
  26. package/harmony/lottie/src/main/ets/common/TextUtils.ets +17 -17
  27. package/harmony/lottie/src/main/ets/generated/components/LottieAnimationView.ts +181 -181
  28. package/harmony/lottie/src/main/ets/generated/components/ts.ts +5 -5
  29. package/harmony/lottie/src/main/ets/generated/index.ets +5 -5
  30. package/harmony/lottie/src/main/ets/generated/ts.ts +6 -6
  31. package/harmony/lottie/src/main/ets/generated/turboModules/ts.ts +5 -5
  32. package/harmony/lottie/src/main/module.json5 +9 -9
  33. package/harmony/lottie/src/main/resources/base/element/string.json +8 -8
  34. package/harmony/lottie/src/main/resources/en_US/element/string.json +8 -8
  35. package/harmony/lottie/src/main/resources/zh_CN/element/string.json +8 -8
  36. package/harmony/lottie/ts.ts +4 -4
  37. package/harmony/lottie.har +0 -0
  38. package/lib/commonjs/LottieAnimationViewNativeComponent.js.map +1 -1
  39. package/lib/commonjs/codegenUtils.js.map +1 -1
  40. package/lib/commonjs/index.js.map +1 -1
  41. package/lib/module/LottieAnimationViewNativeComponent.js.map +1 -1
  42. package/lib/module/codegenUtils.js.map +1 -1
  43. package/lib/module/index.js.map +1 -1
  44. package/package.json +131 -131
  45. package/src/LottieAnimationViewNativeComponent.ts +77 -77
  46. package/src/codegenUtils.ts +8 -8
  47. package/src/index.tsx +7 -7
@@ -1,393 +1,395 @@
1
- // Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
2
- // Use of this source code is governed by a Apache-2.0 license that can be
3
- // found in the LICENSE file.
4
-
5
- import { RNOHContext, RNViewBase } from '@rnoh/react-native-openharmony';
6
- import lottie from '@ohos/lottie';
7
- import { AnimationItem } from '@ohos/lottie';
8
- import http from '@ohos.net.http';
9
- import { colorFiltersItem, LOTTLE_STRING } from './common/AnimationType';
10
- import { AnimationObject, layersItem } from './common/Animation';
11
- import { LottieCompositionCache } from './LottieCompositionCache';
12
- import { convertImageFolder } from './LottieAnimationTools';
13
- import { getHashCode } from './common/TextUtils';
14
- import { RNOHLogger } from "@rnoh/react-native-openharmony/ts";
15
- import { RNC } from './generated';
16
- /**
17
- * @deprecated Use LottieAnimationView.NAME instead
18
- */
19
- export const LOTTIE_TYPE: string = RNC.LottieAnimationView.NAME
20
-
21
- @Component
22
- export struct LottieAnimationView {
23
- public static readonly NAME = RNC.LottieAnimationView.NAME
24
- ctx!: RNOHContext;
25
- tag: number = 0;
26
- private logger!: RNOHLogger
27
- @State descriptorWrapper: RNC.LottieAnimationView.DescriptorWrapper = {} as RNC.LottieAnimationView.DescriptorWrapper;
28
- private unregisterDescriptorChangesListener?: () => void = undefined;
29
- @State @Watch('onStateChanged') progress: number = 0;
30
- @State @Watch('onStateChanged') speed: number = 1;
31
- @State @Watch('onStateChanged') loop: boolean = true;
32
- @State @Watch('onStateChanged') autoPlay: boolean = false;
33
- @State @Watch('onStateChanged') cacheComposition: boolean = true;
34
- private jsonData: AnimationObject | null = {} as AnimationObject;
35
- private jsonDataHashCode: string = '';
36
- private cleanupCommandCallback?: () => void = undefined;
37
- private renderingSettings: RenderingContextSettings = new RenderingContextSettings(true);
38
- private canvasRenderingContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.renderingSettings);
39
- private animateItem: AnimationItem | null = null;
40
- private lottieCache = LottieCompositionCache.getInstance();
41
- private animateKey: string | null = null;
42
- private eventEmitter: RNC.LottieAnimationView.EventEmitter | undefined = undefined
43
-
44
- aboutToAppear() {
45
- // 创建EventEmitter实例,用于处理事件的订阅和触发
46
- this.eventEmitter = new RNC.LottieAnimationView.EventEmitter(this.ctx.rnInstance, this.tag)
47
- this.logger = this.ctx!.logger.clone(RNC.LottieAnimationView.NAME)
48
- this.onDescriptorWrapperChange(this.ctx.descriptorRegistry.findDescriptorWrapperByTag<RNC.LottieAnimationView.DescriptorWrapper>(this.tag)!)
49
- this.commandCallback();
50
- this.subscribeToDescriptorChanges();
51
- lottie.bindContext2dToCoordinator(this.canvasRenderingContext)
52
- this.logger.info(`testlottie:${JSON.stringify(this.descriptorWrapper.props)}`)
53
- }
54
-
55
- private onDescriptorWrapperChange(descriptorWrapper: RNC.LottieAnimationView.DescriptorWrapper) {
56
- this.descriptorWrapper = descriptorWrapper
57
- }
58
-
59
- aboutToDisappear() {
60
- this.cleanupCommandCallback?.();
61
- this.unregisterDescriptorChangesListener?.();
62
- this.destroyAnimation();
63
- if (this.animateKey) {
64
- lottie.destroy(this.animateKey);
65
- }
66
- this.animateKey = null;
67
- lottie.unbindContext2dFromCoordinator(this.canvasRenderingContext)
68
- }
69
-
70
- subscribeToDescriptorChanges(): void {
71
- this.unregisterDescriptorChangesListener = this.ctx.descriptorRegistry.subscribeToDescriptorChanges(this.tag,
72
- (newDescriptor: object) => {
73
- this.descriptorWrapper = (newDescriptor as RNC.LottieAnimationView.DescriptorWrapper);
74
- this.onDescriptorChanged(true);
75
- }
76
- )
77
- }
78
-
79
- onDescriptorChanged(change?: boolean): void {
80
- if (this.descriptorWrapper.props) {
81
- this.handleColorFilters();
82
- this.updateWatchData();
83
- change && this.parseSourceURL();
84
- }
85
- }
86
-
87
- onStateChanged(propName: string): void {
88
- if (this.animateItem == null) {
89
- return;
90
- }
91
- switch (propName) {
92
- case LOTTLE_STRING.progress:
93
- this.setProgress();
94
- break;
95
- case LOTTLE_STRING.speed:
96
- this.setSpeed();
97
- break;
98
- case LOTTLE_STRING.loop: {
99
- this.animateItem.loop = this.loop;
100
- break;
101
- }
102
- case LOTTLE_STRING.autoPlay:
103
- this.setAutoPlay();
104
- break;
105
- default:
106
- break;
107
- }
108
- }
109
-
110
- onAnimationFinish(isCancelled: boolean): void {
111
- this.eventEmitter!.emit("animationFinish", {isCancelled: isCancelled});
112
- }
113
-
114
- onAnimationCancel(): void {
115
- this.onAnimationFinish(true);
116
- }
117
-
118
- onAnimationEnd(): void {
119
- this.onAnimationFinish(false);
120
- }
121
-
122
- cacheProcessing(data: string, isURL?: boolean): void {
123
- const hashCode = getHashCode(data);
124
- if (hashCode === this.jsonDataHashCode) {
125
- return;
126
- }
127
- if (this.descriptorWrapper.props.cacheComposition) {
128
- const tempJsonObj = this.getCacheData(hashCode);
129
- if (tempJsonObj) {
130
- this.jsonData = tempJsonObj;
131
- this.jsonDataHashCode = hashCode;
132
- this.initAnimation();
133
- return;
134
- }
135
- }
136
- if (isURL) {
137
- this.request(data, hashCode);
138
- } else {
139
- this.updateJsonData(data, hashCode);
140
- }
141
- }
142
-
143
- parseSourceURL(): void {
144
- if (!this.descriptorWrapper?.props) {
145
- return;
146
- }
147
- if (this.descriptorWrapper.props.sourceURL &&
148
- this.descriptorWrapper.props.sourceURL.startsWith(LOTTLE_STRING.http)) {
149
- this.cacheProcessing(this.descriptorWrapper.props.sourceURL, true);
150
- } else {
151
- this.descriptorWrapper.props.sourceJson && this.cacheProcessing(this.descriptorWrapper.props.sourceJson);
152
- }
153
- }
154
-
155
- request(url: string, hashCode?: string): void {
156
- this.logger.debug('httpRequest.request url:', url);
157
- const httpRequest = http.createHttp();
158
- httpRequest.request(url, { header: { [LOTTLE_STRING.contentType]: LOTTLE_STRING.json } }, (err, data) => {
159
- if (err == undefined && data != undefined) {
160
- this.logger.debug('httpRequest.request success:', JSON.stringify(data));
161
- const result: Object = data.result;
162
- if (result) {
163
- this.updateJsonData(result as string, hashCode);
164
- }
165
- } else {
166
- this.logger.error('httpRequest.request error:', `errorCode: ${err?.code}`);
167
- }
168
- })
169
- }
170
-
171
- updateWatchData(): void {
172
- this.progress = this.descriptorWrapper.props.progress;
173
- this.speed = this.descriptorWrapper.props.speed;
174
- this.loop = Boolean(this.descriptorWrapper.props.loop);
175
- this.autoPlay = Boolean(this.descriptorWrapper.props.autoPlay);
176
- this.cacheComposition = Boolean(this.descriptorWrapper.props.cacheComposition);
177
- }
178
-
179
- updateJsonData(data: string, hashCode?: string): void {
180
- try {
181
- this.jsonData =
182
- convertImageFolder(JSON.parse(data) as AnimationObject,
183
- this.descriptorWrapper.props.imageAssetsFolder as string);
184
- this.initAnimation();
185
- this.setCacheData(this.jsonData as AnimationObject, hashCode);
186
- } catch (e) {
187
- this.logger.error('updateJsonData,source parse error');
188
- this.jsonData = null;
189
- }
190
- }
191
-
192
- setCacheData(jsonObj: AnimationObject, key?: string): void {
193
- if (key) {
194
- this.lottieCache.set(key, jsonObj);
195
- this.jsonDataHashCode = key;
196
- }
197
- }
198
-
199
- getCacheData(key: string): AnimationObject | null {
200
- return this.lottieCache.get(key);
201
- }
202
-
203
- initAnimation(): void {
204
- if (this.jsonData == null) {
205
- return;
206
- }
207
- this.loadAnimation();
208
- this.setSpeed();
209
- this.addEventFrameListener();
210
- }
211
-
212
- destroyAnimation(): void {
213
- if (this.animateItem == null) {
214
- return;
215
- }
216
- if (this.animateItem.isPaused === false) {
217
- this.onAnimationEnd();
218
- }
219
- lottie.destroy(this.animateKey);
220
- this.animateItem = null;
221
- }
222
-
223
- loadAnimation(): void {
224
- this.destroyAnimation();
225
- this.animateKey = `${this.tag}${new Date().getTime()}`;
226
- this.animateItem = lottie.loadAnimation({
227
- container: this.canvasRenderingContext,
228
- renderer: LOTTLE_STRING.canvas,
229
- name: this.animateKey,
230
- loop: Boolean(this.descriptorWrapper.props.loop),
231
- autoplay: Boolean(this.descriptorWrapper.props.autoPlay),
232
- animationData: this.jsonData
233
- });
234
- this.animateItem?.addEventListener(LOTTLE_STRING.DOMLoaded, () => {
235
- try{
236
- let upperResizeMode =
237
- this.descriptorWrapper.props?.resizeMode?.replace(this.descriptorWrapper.props.resizeMode[0],
238
- this.descriptorWrapper.props.resizeMode[0].toUpperCase())
239
- this.eventEmitter!.emit("animationLoaded", {});
240
- this.animateItem?.setContentMode(upperResizeMode);
241
- this.onDescriptorChanged();
242
- }catch(e) {
243
- this.logger.error('DOMLoaded catch error:', e);
244
- }
245
- })
246
- this.animateItem?.addEventListener(LOTTLE_STRING.data_failed, () => {
247
- this.eventEmitter!.emit("animationFailure", {error: 'data_failed'});
248
- })
249
- }
250
-
251
- handleColorFilters(): void {
252
- const layersData: Array<layersItem> = this.jsonData?.layers ?? [];
253
- const colorFiltersData: Array<colorFiltersItem> =
254
- (this.descriptorWrapper.props.colorFilters ?? []) as Array<colorFiltersItem>;
255
- for (const item of colorFiltersData) {
256
- const index: number = layersData.findIndex((layersItem: layersItem) => layersItem?.nm === item?.keypath);
257
- const color: Array<number> = this.getColorByColorFilters(item);
258
- const isIndex: boolean = index !== (-1);
259
- const isColor: boolean = color.length === 3;
260
- if (isIndex && isColor) {
261
- this.animateItem?.changeColor(color, index + 1);
262
- this.logger.debug('colorFilters success:', item?.keypath);
263
- } else {
264
- this.logger.error('colorFilters fail:not find keyPath', item?.keypath);
265
- }
266
- }
267
- }
268
-
269
- getColorByColorFilters(colorFiltersItem: colorFiltersItem): Array<number> {
270
- const color: string = colorFiltersItem?.color;
271
- if (color) {
272
- const processColor: number = Number(color);
273
- const r: number = (processColor >> 16) & 0xff;
274
- const g: number = (processColor >> 8) & 0xff;
275
- const b: number = processColor & 0xff;
276
- this.logger.debug(`getColorByColorFilters, r=${r}, g=${g}, b=${b}`);
277
- return [r, g, b];
278
- }
279
- this.logger.error('colorFilters fail:not find color', colorFiltersItem?.keypath);
280
- return [];
281
- }
282
-
283
- setProgress(): void {
284
- const frame = this.getAnimateFrame();
285
- this.animateItem?.goToAndStop(frame, true);
286
- }
287
-
288
- setSpeed(): void {
289
- this.animateItem?.setSpeed(this.descriptorWrapper.props.speed);
290
- }
291
-
292
- setAutoPlay(): void {
293
- this.animateItem?.goToAndPlay(this.getAnimateFrame(), true);
294
- }
295
-
296
- getAnimateFrame(): number {
297
- const firstFrame: number = this.animateItem?.firstFrame ?? 0;
298
- const totalFrames: number = this.animateItem?.totalFrames ?? 0;
299
- return Math.ceil(firstFrame + this.descriptorWrapper.props.progress * totalFrames);
300
- }
301
-
302
- addEventFrameListener(): void {
303
- this.completeEvent();
304
- }
305
-
306
- completeEvent(): void {
307
- //动画播放结束且不再播放动画触发
308
- this.animateItem?.addEventListener(LOTTLE_STRING.complete, () => {
309
- this.onAnimationFinish(false);
310
- })
311
- }
312
-
313
- commandCallback(): void {
314
- this.cleanupCommandCallback = this.ctx.componentCommandReceiver.registerCommandCallback(
315
- this.tag,
316
- (command: string, args: object) => {
317
- if (this.animateItem == null) {
318
- return;
319
- }
320
- switch (command) {
321
- case LOTTLE_STRING.play:
322
- this.play(args as number[]);
323
- break;
324
- case LOTTLE_STRING.reset:
325
- this.reset();
326
- break;
327
- case LOTTLE_STRING.pause:
328
- this.pause();
329
- break;
330
- case LOTTLE_STRING.resume:
331
- this.resume();
332
- break;
333
- default:
334
- this.logger.warn('not find command');
335
- break;
336
- }
337
- })
338
- }
339
-
340
- play(args: number[]): void {
341
- const startFrame = args[0];
342
- const endFrame = args[1];
343
- this.animateItem?.stop();
344
- if (this.progress !== 0) {
345
- this.progress = 0;
346
- this.animateItem?.goToAndPlay(this.getAnimateFrame(), true);
347
- return;
348
- }
349
- if (args.length > 1 && startFrame != -1 && endFrame != -1) {
350
- if (startFrame > endFrame) {
351
- this.animateItem?.setSegment(endFrame, startFrame);
352
- if (this.descriptorWrapper.props.speed > 0) {
353
- this.animateItem?.setDirection(-1);
354
- }
355
- } else {
356
- this.animateItem?.setSegment(startFrame, endFrame);
357
- if (this.descriptorWrapper.props.speed < 0) {
358
- this.animateItem?.setDirection(-1);
359
- }
360
- }
361
- }
362
- this.animateItem?.play();
363
- }
364
-
365
- reset(): void {
366
- this.animateItem?.stop();
367
- this.onAnimationEnd();
368
- }
369
-
370
- pause(): void {
371
- this.animateItem?.pause();
372
- }
373
-
374
- resume(): void {
375
- if (this.animateItem?.isPaused) {
376
- this.animateItem?.togglePause();
377
- }
378
- }
379
-
380
- resize(): void {
381
- this.animateItem?.resize();
382
- }
383
-
384
- build() {
385
- RNViewBase({ ctx: this.ctx, tag: this.tag }) {
386
- Canvas(this.canvasRenderingContext)
387
- .width('100%')
388
- .height('100%')
389
- .onReady(() => this.parseSourceURL())
390
- .onAreaChange(() => this.resize())
391
- }
392
- }
393
- }
1
+ // Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved
2
+ // Use of this source code is governed by a Apache-2.0 license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import { RNOHContext, RNViewBase } from '@rnoh/react-native-openharmony';
6
+ import lottie from '@ohos/lottie';
7
+ import { AnimationItem } from '@ohos/lottie';
8
+ import http from '@ohos.net.http';
9
+ import { colorFiltersItem, LOTTLE_STRING } from './common/AnimationType';
10
+ import { AnimationObject, layersItem } from './common/Animation';
11
+ import { LottieCompositionCache } from './LottieCompositionCache';
12
+ import { convertImageFolder } from './LottieAnimationTools';
13
+ import { getHashCode } from './common/TextUtils';
14
+ import { RNOHLogger } from "@rnoh/react-native-openharmony/ts";
15
+ import { RNC } from './generated';
16
+ /**
17
+ * @deprecated Use LottieAnimationView.NAME instead
18
+ */
19
+ export const LOTTIE_TYPE: string = RNC.LottieAnimationView.NAME
20
+
21
+ @Component
22
+ export struct LottieAnimationView {
23
+ public static readonly NAME = RNC.LottieAnimationView.NAME
24
+ ctx!: RNOHContext;
25
+ tag: number = 0;
26
+ private logger!: RNOHLogger
27
+ @State descriptorWrapper: RNC.LottieAnimationView.DescriptorWrapper = {} as RNC.LottieAnimationView.DescriptorWrapper;
28
+ private unregisterDescriptorChangesListener?: () => void = undefined;
29
+ @State @Watch('onStateChanged') progress: number = 0;
30
+ @State @Watch('onStateChanged') speed: number = 1;
31
+ @State @Watch('onStateChanged') loop: boolean = true;
32
+ @State @Watch('onStateChanged') autoPlay: boolean = false;
33
+ @State @Watch('onStateChanged') cacheComposition: boolean = true;
34
+ private jsonData: AnimationObject | null = {} as AnimationObject;
35
+ private jsonDataHashCode: string = '';
36
+ private cleanupCommandCallback?: () => void = undefined;
37
+ private renderingSettings: RenderingContextSettings = new RenderingContextSettings(true);
38
+ private canvasRenderingContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.renderingSettings);
39
+ private animateItem: AnimationItem | null = null;
40
+ private lottieCache = LottieCompositionCache.getInstance();
41
+ private animateKey: string | null = null;
42
+ private eventEmitter: RNC.LottieAnimationView.EventEmitter | undefined = undefined
43
+
44
+ aboutToAppear() {
45
+ // 创建EventEmitter实例,用于处理事件的订阅和触发
46
+ this.eventEmitter = new RNC.LottieAnimationView.EventEmitter(this.ctx.rnInstance, this.tag)
47
+ this.logger = this.ctx!.logger.clone(RNC.LottieAnimationView.NAME)
48
+ this.onDescriptorWrapperChange(this.ctx.descriptorRegistry.findDescriptorWrapperByTag<RNC.LottieAnimationView.DescriptorWrapper>(this.tag)!)
49
+ this.commandCallback();
50
+ this.subscribeToDescriptorChanges();
51
+ lottie.bindContext2dToCoordinator(this.canvasRenderingContext)
52
+ this.logger.info(`testlottie:${JSON.stringify(this.descriptorWrapper.props)}`)
53
+ }
54
+
55
+ private onDescriptorWrapperChange(descriptorWrapper: RNC.LottieAnimationView.DescriptorWrapper) {
56
+ this.descriptorWrapper = descriptorWrapper
57
+ }
58
+
59
+ aboutToDisappear() {
60
+ this.cleanupCommandCallback?.();
61
+ this.unregisterDescriptorChangesListener?.();
62
+ this.destroyAnimation();
63
+ if (this.animateKey) {
64
+ lottie.destroy(this.animateKey);
65
+ }
66
+ this.animateKey = null;
67
+ lottie.unbindContext2dFromCoordinator(this.canvasRenderingContext)
68
+ }
69
+
70
+ subscribeToDescriptorChanges(): void {
71
+ this.unregisterDescriptorChangesListener = this.ctx.descriptorRegistry.subscribeToDescriptorChanges(this.tag,
72
+ (newDescriptor: object) => {
73
+ this.descriptorWrapper = (newDescriptor as RNC.LottieAnimationView.DescriptorWrapper);
74
+ this.onDescriptorChanged(true);
75
+ }
76
+ )
77
+ }
78
+
79
+ onDescriptorChanged(change?: boolean): void {
80
+ if (this.descriptorWrapper.props) {
81
+ this.handleColorFilters();
82
+ this.updateWatchData();
83
+ change && this.parseSourceURL();
84
+ }
85
+ }
86
+
87
+ onStateChanged(propName: string): void {
88
+ if (this.animateItem == null) {
89
+ return;
90
+ }
91
+ switch (propName) {
92
+ case LOTTLE_STRING.progress:
93
+ this.setProgress();
94
+ break;
95
+ case LOTTLE_STRING.speed:
96
+ this.setSpeed();
97
+ break;
98
+ case LOTTLE_STRING.loop: {
99
+ this.animateItem.loop = this.loop;
100
+ break;
101
+ }
102
+ case LOTTLE_STRING.autoPlay:
103
+ this.setAutoPlay();
104
+ break;
105
+ default:
106
+ break;
107
+ }
108
+ }
109
+
110
+ onAnimationFinish(isCancelled: boolean): void {
111
+ this.eventEmitter!.emit("animationFinish", {isCancelled: isCancelled});
112
+ }
113
+
114
+ onAnimationCancel(): void {
115
+ this.onAnimationFinish(true);
116
+ }
117
+
118
+ onAnimationEnd(): void {
119
+ this.onAnimationFinish(false);
120
+ }
121
+
122
+ cacheProcessing(data: string, isURL?: boolean): void {
123
+ const hashCode = getHashCode(data);
124
+ if (hashCode === this.jsonDataHashCode) {
125
+ return;
126
+ }
127
+ if (this.descriptorWrapper.props.cacheComposition) {
128
+ const tempJsonObj = this.getCacheData(hashCode);
129
+ if (tempJsonObj) {
130
+ this.jsonData = tempJsonObj;
131
+ this.jsonDataHashCode = hashCode;
132
+ this.initAnimation();
133
+ return;
134
+ }
135
+ }
136
+ if (isURL) {
137
+ this.request(data, hashCode);
138
+ } else {
139
+ this.updateJsonData(data, hashCode);
140
+ }
141
+ }
142
+
143
+ parseSourceURL(): void {
144
+ if (!this.descriptorWrapper?.props) {
145
+ return;
146
+ }
147
+ if (this.descriptorWrapper.props.sourceURL &&
148
+ this.descriptorWrapper.props.sourceURL.startsWith(LOTTLE_STRING.http)) {
149
+ this.cacheProcessing(this.descriptorWrapper.props.sourceURL, true);
150
+ } else {
151
+ this.descriptorWrapper.props.sourceJson && this.cacheProcessing(this.descriptorWrapper.props.sourceJson);
152
+ }
153
+ }
154
+
155
+ request(url: string, hashCode?: string): void {
156
+ this.logger.debug('httpRequest.request url:', url);
157
+ const httpRequest = http.createHttp();
158
+ httpRequest.request(url, { header: { [LOTTLE_STRING.contentType]: LOTTLE_STRING.json } }, (err, data) => {
159
+ if (err == undefined && data != undefined) {
160
+ this.logger.debug('httpRequest.request success:', JSON.stringify(data));
161
+ const result: Object = data.result;
162
+ if (result) {
163
+ this.updateJsonData(result as string, hashCode);
164
+ }
165
+ } else {
166
+ this.logger.error('httpRequest.request error:', `errorCode: ${err?.code}`);
167
+ }
168
+ })
169
+ }
170
+
171
+ updateWatchData(): void {
172
+ this.progress = this.descriptorWrapper.props.progress;
173
+ this.speed = this.descriptorWrapper.props.speed;
174
+ this.loop = Boolean(this.descriptorWrapper.props.loop);
175
+ this.autoPlay = Boolean(this.descriptorWrapper.props.autoPlay);
176
+ this.cacheComposition = Boolean(this.descriptorWrapper.props.cacheComposition);
177
+ }
178
+
179
+ updateJsonData(data: string, hashCode?: string): void {
180
+ try {
181
+ this.jsonData =
182
+ convertImageFolder(JSON.parse(data) as AnimationObject,
183
+ this.descriptorWrapper.props.imageAssetsFolder as string);
184
+ this.initAnimation();
185
+ this.setCacheData(this.jsonData as AnimationObject, hashCode);
186
+ } catch (e) {
187
+ this.logger.error('updateJsonData,source parse error');
188
+ this.jsonData = null;
189
+ }
190
+ }
191
+
192
+ setCacheData(jsonObj: AnimationObject, key?: string): void {
193
+ if (key) {
194
+ this.lottieCache.set(key, jsonObj);
195
+ this.jsonDataHashCode = key;
196
+ }
197
+ }
198
+
199
+ getCacheData(key: string): AnimationObject | null {
200
+ return this.lottieCache.get(key);
201
+ }
202
+
203
+ initAnimation(): void {
204
+ if (this.jsonData == null) {
205
+ return;
206
+ }
207
+ this.loadAnimation();
208
+ this.setSpeed();
209
+ this.addEventFrameListener();
210
+ }
211
+
212
+ destroyAnimation(): void {
213
+ if (this.animateItem == null) {
214
+ return;
215
+ }
216
+ if (this.animateItem.isPaused === false) {
217
+ this.onAnimationEnd();
218
+ }
219
+ lottie.destroy(this.animateKey);
220
+ this.animateItem = null;
221
+ }
222
+
223
+ loadAnimation(): void {
224
+ this.destroyAnimation();
225
+ this.animateKey = `${this.tag}${new Date().getTime()}`;
226
+ this.animateItem = lottie.loadAnimation({
227
+ container: this.canvasRenderingContext,
228
+ renderer: LOTTLE_STRING.canvas,
229
+ name: this.animateKey,
230
+ loop: Boolean(this.descriptorWrapper.props.loop),
231
+ autoplay: Boolean(this.descriptorWrapper.props.autoPlay),
232
+ animationData: this.jsonData
233
+ });
234
+ this.animateItem?.addEventListener(LOTTLE_STRING.DOMLoaded, () => {
235
+ try{
236
+ let upperResizeMode =
237
+ this.descriptorWrapper.props?.resizeMode?.replace(this.descriptorWrapper.props.resizeMode[0],
238
+ this.descriptorWrapper.props.resizeMode[0].toUpperCase())
239
+ this.eventEmitter!.emit("animationLoaded", {});
240
+ this.animateItem?.setContentMode(upperResizeMode);
241
+ this.onDescriptorChanged();
242
+ }catch(e) {
243
+ this.logger.error('DOMLoaded catch error:', e);
244
+ }
245
+ })
246
+ this.animateItem?.addEventListener(LOTTLE_STRING.data_failed, () => {
247
+ this.eventEmitter!.emit("animationFailure", {error: 'data_failed'});
248
+ })
249
+ this.animateItem?.addEventListener('error', () => {
250
+ this.eventEmitter!.emit("animationFailure", {error: 'data_failed'});
251
+ })
252
+ }
253
+
254
+ handleColorFilters(): void {
255
+ const layersData: Array<layersItem> = this.jsonData?.layers ?? [];
256
+ const colorFiltersData: Array<colorFiltersItem> =
257
+ (this.descriptorWrapper.props.colorFilters ?? []) as Array<colorFiltersItem>;
258
+ for (const item of colorFiltersData) {
259
+ const index: number = layersData.findIndex((layersItem: layersItem) => layersItem?.nm === item?.keypath);
260
+ const color: Array<number> = this.getColorByColorFilters(item);
261
+ const isIndex: boolean = index !== (-1);
262
+ const isColor: boolean = color.length === 3;
263
+ if (isIndex && isColor) {
264
+ this.animateItem?.changeColor(color, index + 1);
265
+ this.logger.debug('colorFilters success:', item?.keypath);
266
+ } else {
267
+ this.logger.error('colorFilters fail:not find keyPath', item?.keypath);
268
+ }
269
+ }
270
+ }
271
+
272
+ getColorByColorFilters(colorFiltersItem: colorFiltersItem): Array<number> {
273
+ const color: string = colorFiltersItem?.color;
274
+ if (color) {
275
+ const processColor: number = Number(color);
276
+ const r: number = (processColor >> 16) & 0xff;
277
+ const g: number = (processColor >> 8) & 0xff;
278
+ const b: number = processColor & 0xff;
279
+ this.logger.debug(`getColorByColorFilters, r=${r}, g=${g}, b=${b}`);
280
+ return [r, g, b];
281
+ }
282
+ this.logger.error('colorFilters fail:not find color', colorFiltersItem?.keypath);
283
+ return [];
284
+ }
285
+
286
+ setProgress(): void {
287
+ const frame = this.getAnimateFrame();
288
+ this.animateItem?.goToAndStop(frame, true);
289
+ }
290
+
291
+ setSpeed(): void {
292
+ this.animateItem?.setSpeed(this.descriptorWrapper.props.speed);
293
+ }
294
+
295
+ setAutoPlay(): void {
296
+ this.animateItem?.goToAndPlay(this.getAnimateFrame(), true);
297
+ }
298
+
299
+ getAnimateFrame(): number {
300
+ const totalFrames: number = this.animateItem?.totalFrames ?? 0;
301
+ return Math.ceil(this.descriptorWrapper.props.progress * totalFrames);
302
+ }
303
+
304
+ addEventFrameListener(): void {
305
+ this.completeEvent();
306
+ }
307
+
308
+ completeEvent(): void {
309
+ //动画播放结束且不再播放动画触发
310
+ this.animateItem?.addEventListener(LOTTLE_STRING.complete, () => {
311
+ this.onAnimationFinish(false);
312
+ })
313
+ }
314
+
315
+ commandCallback(): void {
316
+ this.cleanupCommandCallback = this.ctx.componentCommandReceiver.registerCommandCallback(
317
+ this.tag,
318
+ (command: string, args: object) => {
319
+ if (this.animateItem == null) {
320
+ return;
321
+ }
322
+ switch (command) {
323
+ case LOTTLE_STRING.play:
324
+ this.play(args as number[]);
325
+ break;
326
+ case LOTTLE_STRING.reset:
327
+ this.reset();
328
+ break;
329
+ case LOTTLE_STRING.pause:
330
+ this.pause();
331
+ break;
332
+ case LOTTLE_STRING.resume:
333
+ this.resume();
334
+ break;
335
+ default:
336
+ this.logger.warn('not find command');
337
+ break;
338
+ }
339
+ })
340
+ }
341
+
342
+ play(args: number[]): void {
343
+ const startFrame = args[0];
344
+ const endFrame = args[1];
345
+ this.animateItem?.stop();
346
+ if (this.progress !== 0) {
347
+ this.progress = 0;
348
+ this.animateItem?.goToAndPlay(this.getAnimateFrame(), true);
349
+ return;
350
+ }
351
+ if (args.length > 1 && startFrame != -1 && endFrame != -1) {
352
+ if (startFrame > endFrame) {
353
+ this.animateItem?.setSegment(endFrame, startFrame);
354
+ if (this.descriptorWrapper.props.speed > 0) {
355
+ this.animateItem?.setDirection(-1);
356
+ }
357
+ } else {
358
+ this.animateItem?.setSegment(startFrame, endFrame);
359
+ if (this.descriptorWrapper.props.speed < 0) {
360
+ this.animateItem?.setDirection(-1);
361
+ }
362
+ }
363
+ }
364
+ this.animateItem?.play();
365
+ }
366
+
367
+ reset(): void {
368
+ this.animateItem?.stop();
369
+ this.onAnimationEnd();
370
+ }
371
+
372
+ pause(): void {
373
+ this.animateItem?.pause();
374
+ }
375
+
376
+ resume(): void {
377
+ if (this.animateItem?.isPaused) {
378
+ this.animateItem?.togglePause();
379
+ }
380
+ }
381
+
382
+ resize(): void {
383
+ this.animateItem?.resize();
384
+ }
385
+
386
+ build() {
387
+ RNViewBase({ ctx: this.ctx, tag: this.tag }) {
388
+ Canvas(this.canvasRenderingContext)
389
+ .width('100%')
390
+ .height('100%')
391
+ .onReady(() => this.parseSourceURL())
392
+ .onAreaChange(() => this.resize())
393
+ }
394
+ }
395
+ }