@idealyst/lottie 1.2.29
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/package.json +69 -0
- package/src/Lottie.native.tsx +191 -0
- package/src/Lottie.tsx +250 -0
- package/src/index.native.ts +48 -0
- package/src/index.ts +46 -0
- package/src/types.ts +226 -0
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@idealyst/lottie",
|
|
3
|
+
"version": "1.2.29",
|
|
4
|
+
"description": "Cross-platform Lottie animation component for React and React Native",
|
|
5
|
+
"readme": "README.md",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"module": "src/index.ts",
|
|
8
|
+
"types": "src/index.ts",
|
|
9
|
+
"react-native": "src/index.native.ts",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/IdealystIO/idealyst-framework.git",
|
|
13
|
+
"directory": "packages/lottie"
|
|
14
|
+
},
|
|
15
|
+
"author": "Idealyst <contact@idealyst.io>",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"react-native": "./src/index.native.ts",
|
|
23
|
+
"import": "./src/index.ts",
|
|
24
|
+
"require": "./src/index.ts",
|
|
25
|
+
"types": "./src/index.ts"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"prepublishOnly": "echo 'Publishing TypeScript source directly'",
|
|
30
|
+
"publish:npm": "npm publish"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"lottie-react-native": ">=6.0.0",
|
|
34
|
+
"lottie-web": ">=5.0.0",
|
|
35
|
+
"react": ">=16.8.0",
|
|
36
|
+
"react-native": ">=0.60.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"lottie-react-native": {
|
|
40
|
+
"optional": true
|
|
41
|
+
},
|
|
42
|
+
"lottie-web": {
|
|
43
|
+
"optional": true
|
|
44
|
+
},
|
|
45
|
+
"react-native": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/react": "^19.1.0",
|
|
51
|
+
"lottie-react-native": "^7.2.2",
|
|
52
|
+
"lottie-web": "^5.12.2",
|
|
53
|
+
"react": "^19.1.0",
|
|
54
|
+
"react-native": "^0.80.1",
|
|
55
|
+
"typescript": "^5.0.0"
|
|
56
|
+
},
|
|
57
|
+
"files": [
|
|
58
|
+
"src",
|
|
59
|
+
"README.md"
|
|
60
|
+
],
|
|
61
|
+
"keywords": [
|
|
62
|
+
"react",
|
|
63
|
+
"react-native",
|
|
64
|
+
"lottie",
|
|
65
|
+
"animation",
|
|
66
|
+
"cross-platform",
|
|
67
|
+
"after-effects"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lottie - Native implementation
|
|
3
|
+
*
|
|
4
|
+
* Uses lottie-react-native for rendering Lottie animations on React Native.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { forwardRef, useImperativeHandle, useRef, useCallback } from 'react';
|
|
8
|
+
import LottieView from 'lottie-react-native';
|
|
9
|
+
import type { LottieProps, LottieRef, LottieJSON, LottieSource } from './types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Lottie animation component for React Native.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import { Lottie } from '@idealyst/lottie';
|
|
17
|
+
*
|
|
18
|
+
* // With require (recommended for native)
|
|
19
|
+
* <Lottie source={require('./animation.json')} autoPlay loop />
|
|
20
|
+
*
|
|
21
|
+
* // With imported JSON
|
|
22
|
+
* import animationData from './animation.json';
|
|
23
|
+
* <Lottie source={animationData} autoPlay />
|
|
24
|
+
*
|
|
25
|
+
* // With URL
|
|
26
|
+
* <Lottie source={{ uri: 'https://example.com/animation.json' }} autoPlay />
|
|
27
|
+
*
|
|
28
|
+
* // With ref for control
|
|
29
|
+
* const lottieRef = useRef<LottieRef>(null);
|
|
30
|
+
* <Lottie ref={lottieRef} source={require('./animation.json')} />
|
|
31
|
+
* lottieRef.current?.play();
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export const Lottie = forwardRef<LottieRef, LottieProps>((props, ref) => {
|
|
35
|
+
const {
|
|
36
|
+
source,
|
|
37
|
+
autoPlay = true,
|
|
38
|
+
loop = false,
|
|
39
|
+
speed = 1,
|
|
40
|
+
style,
|
|
41
|
+
resizeMode = 'contain',
|
|
42
|
+
progress,
|
|
43
|
+
onComplete,
|
|
44
|
+
onLoad,
|
|
45
|
+
onError,
|
|
46
|
+
onAnimationUpdate,
|
|
47
|
+
visible = true,
|
|
48
|
+
testID,
|
|
49
|
+
} = props;
|
|
50
|
+
|
|
51
|
+
const lottieRef = useRef<LottieView>(null);
|
|
52
|
+
const isPlayingRef = useRef(autoPlay);
|
|
53
|
+
const totalFramesRef = useRef(0);
|
|
54
|
+
const frameRateRef = useRef(30);
|
|
55
|
+
|
|
56
|
+
// Convert source to lottie-react-native format
|
|
57
|
+
const getSource = (): any => {
|
|
58
|
+
if (typeof source === 'string') {
|
|
59
|
+
// URL string - wrap in uri object
|
|
60
|
+
return { uri: source };
|
|
61
|
+
}
|
|
62
|
+
if (typeof source === 'object' && 'uri' in source) {
|
|
63
|
+
// Already in { uri: string } format
|
|
64
|
+
return source;
|
|
65
|
+
}
|
|
66
|
+
// Direct JSON object or require()
|
|
67
|
+
return source;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Handle animation load
|
|
71
|
+
const handleLoad = useCallback(() => {
|
|
72
|
+
// Try to get animation info from the ref
|
|
73
|
+
// Note: lottie-react-native doesn't expose these directly in all cases
|
|
74
|
+
onLoad?.();
|
|
75
|
+
}, [onLoad]);
|
|
76
|
+
|
|
77
|
+
// Handle animation finish
|
|
78
|
+
const handleFinish = useCallback(
|
|
79
|
+
(isCancelled: boolean) => {
|
|
80
|
+
if (!isCancelled) {
|
|
81
|
+
isPlayingRef.current = false;
|
|
82
|
+
onComplete?.();
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
[onComplete]
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// Handle animation failure
|
|
89
|
+
const handleFailure = useCallback(
|
|
90
|
+
(error: any) => {
|
|
91
|
+
onError?.(new Error(error?.message || 'Failed to load animation'));
|
|
92
|
+
},
|
|
93
|
+
[onError]
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Imperative handle for ref methods
|
|
97
|
+
useImperativeHandle(
|
|
98
|
+
ref,
|
|
99
|
+
() => ({
|
|
100
|
+
play: () => {
|
|
101
|
+
isPlayingRef.current = true;
|
|
102
|
+
lottieRef.current?.play();
|
|
103
|
+
},
|
|
104
|
+
pause: () => {
|
|
105
|
+
isPlayingRef.current = false;
|
|
106
|
+
lottieRef.current?.pause();
|
|
107
|
+
},
|
|
108
|
+
stop: () => {
|
|
109
|
+
isPlayingRef.current = false;
|
|
110
|
+
lottieRef.current?.reset();
|
|
111
|
+
},
|
|
112
|
+
reset: () => {
|
|
113
|
+
lottieRef.current?.reset();
|
|
114
|
+
},
|
|
115
|
+
setProgress: (value: number) => {
|
|
116
|
+
// lottie-react-native uses progress prop for this
|
|
117
|
+
// For imperative control, we need to use play with segments
|
|
118
|
+
if (lottieRef.current && totalFramesRef.current > 0) {
|
|
119
|
+
const frame = Math.floor(value * totalFramesRef.current);
|
|
120
|
+
lottieRef.current.play(frame, frame);
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
goToAndStop: (frame: number, isFrame = true) => {
|
|
124
|
+
isPlayingRef.current = false;
|
|
125
|
+
if (lottieRef.current) {
|
|
126
|
+
// Play to the frame and immediately pause
|
|
127
|
+
lottieRef.current.play(frame, frame);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
goToAndPlay: (frame: number, isFrame = true) => {
|
|
131
|
+
isPlayingRef.current = true;
|
|
132
|
+
if (lottieRef.current) {
|
|
133
|
+
lottieRef.current.play(frame, undefined);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
setSpeed: (value: number) => {
|
|
137
|
+
// Speed is controlled via prop, not imperatively in lottie-react-native
|
|
138
|
+
console.warn('setSpeed: Use the speed prop instead for native');
|
|
139
|
+
},
|
|
140
|
+
setDirection: (direction: 1 | -1) => {
|
|
141
|
+
// Direction is controlled via speed sign
|
|
142
|
+
console.warn('setDirection: Use negative speed for reverse playback');
|
|
143
|
+
},
|
|
144
|
+
playSegments: (startFrame: number, endFrame: number, forceFlag = false) => {
|
|
145
|
+
isPlayingRef.current = true;
|
|
146
|
+
lottieRef.current?.play(startFrame, endFrame);
|
|
147
|
+
},
|
|
148
|
+
getCurrentFrame: () => {
|
|
149
|
+
// Not directly available in lottie-react-native
|
|
150
|
+
return 0;
|
|
151
|
+
},
|
|
152
|
+
getTotalFrames: () => {
|
|
153
|
+
return totalFramesRef.current;
|
|
154
|
+
},
|
|
155
|
+
getDuration: () => {
|
|
156
|
+
return totalFramesRef.current / frameRateRef.current;
|
|
157
|
+
},
|
|
158
|
+
isPlaying: () => {
|
|
159
|
+
return isPlayingRef.current;
|
|
160
|
+
},
|
|
161
|
+
destroy: () => {
|
|
162
|
+
// React Native handles cleanup automatically
|
|
163
|
+
lottieRef.current?.reset();
|
|
164
|
+
},
|
|
165
|
+
}),
|
|
166
|
+
[]
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
if (!visible) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<LottieView
|
|
175
|
+
ref={lottieRef}
|
|
176
|
+
source={getSource()}
|
|
177
|
+
autoPlay={autoPlay}
|
|
178
|
+
loop={typeof loop === 'boolean' ? loop : true}
|
|
179
|
+
speed={speed}
|
|
180
|
+
style={style}
|
|
181
|
+
resizeMode={resizeMode}
|
|
182
|
+
progress={progress}
|
|
183
|
+
onAnimationLoaded={handleLoad}
|
|
184
|
+
onAnimationFinish={handleFinish}
|
|
185
|
+
onAnimationFailure={handleFailure}
|
|
186
|
+
testID={testID}
|
|
187
|
+
/>
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
Lottie.displayName = 'Lottie';
|
package/src/Lottie.tsx
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lottie - Web implementation
|
|
3
|
+
*
|
|
4
|
+
* Uses lottie-web for rendering Lottie animations on the web.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, {
|
|
8
|
+
forwardRef,
|
|
9
|
+
useImperativeHandle,
|
|
10
|
+
useRef,
|
|
11
|
+
useEffect,
|
|
12
|
+
useCallback,
|
|
13
|
+
useState,
|
|
14
|
+
} from 'react';
|
|
15
|
+
import lottie, { AnimationItem } from 'lottie-web';
|
|
16
|
+
import type { LottieProps, LottieRef, LottieJSON } from './types';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Lottie animation component for web.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* import { Lottie } from '@idealyst/lottie';
|
|
24
|
+
*
|
|
25
|
+
* // With imported JSON
|
|
26
|
+
* import animationData from './animation.json';
|
|
27
|
+
* <Lottie source={animationData} autoPlay loop />
|
|
28
|
+
*
|
|
29
|
+
* // With URL
|
|
30
|
+
* <Lottie source="https://example.com/animation.json" autoPlay />
|
|
31
|
+
*
|
|
32
|
+
* // With ref for control
|
|
33
|
+
* const lottieRef = useRef<LottieRef>(null);
|
|
34
|
+
* <Lottie ref={lottieRef} source={animationData} />
|
|
35
|
+
* lottieRef.current?.play();
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export const Lottie = forwardRef<LottieRef, LottieProps>((props, ref) => {
|
|
39
|
+
const {
|
|
40
|
+
source,
|
|
41
|
+
autoPlay = true,
|
|
42
|
+
loop = false,
|
|
43
|
+
speed = 1,
|
|
44
|
+
style,
|
|
45
|
+
resizeMode = 'contain',
|
|
46
|
+
progress,
|
|
47
|
+
onComplete,
|
|
48
|
+
onLoad,
|
|
49
|
+
onError,
|
|
50
|
+
onAnimationUpdate,
|
|
51
|
+
visible = true,
|
|
52
|
+
testID,
|
|
53
|
+
} = props;
|
|
54
|
+
|
|
55
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
56
|
+
const animationRef = useRef<AnimationItem | null>(null);
|
|
57
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
58
|
+
|
|
59
|
+
// Convert loop prop to lottie-web format
|
|
60
|
+
const loopValue = typeof loop === 'number' ? loop : loop;
|
|
61
|
+
|
|
62
|
+
// Load animation
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (!containerRef.current || !visible) return;
|
|
65
|
+
|
|
66
|
+
const loadAnimation = async () => {
|
|
67
|
+
try {
|
|
68
|
+
// Determine animation data
|
|
69
|
+
let animationData: LottieJSON | undefined;
|
|
70
|
+
let path: string | undefined;
|
|
71
|
+
|
|
72
|
+
if (typeof source === 'string') {
|
|
73
|
+
// URL string
|
|
74
|
+
path = source;
|
|
75
|
+
} else if (typeof source === 'object' && 'uri' in source) {
|
|
76
|
+
// { uri: string } format
|
|
77
|
+
path = source.uri;
|
|
78
|
+
} else {
|
|
79
|
+
// Direct JSON object
|
|
80
|
+
animationData = source as LottieJSON;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Destroy previous animation if exists
|
|
84
|
+
if (animationRef.current) {
|
|
85
|
+
animationRef.current.destroy();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Create new animation
|
|
89
|
+
const anim = lottie.loadAnimation({
|
|
90
|
+
container: containerRef.current!,
|
|
91
|
+
renderer: 'svg',
|
|
92
|
+
loop: loopValue,
|
|
93
|
+
autoplay: autoPlay,
|
|
94
|
+
animationData,
|
|
95
|
+
path,
|
|
96
|
+
rendererSettings: {
|
|
97
|
+
preserveAspectRatio:
|
|
98
|
+
resizeMode === 'cover'
|
|
99
|
+
? 'xMidYMid slice'
|
|
100
|
+
: resizeMode === 'contain'
|
|
101
|
+
? 'xMidYMid meet'
|
|
102
|
+
: 'xMidYMid meet',
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
animationRef.current = anim;
|
|
107
|
+
|
|
108
|
+
// Set speed
|
|
109
|
+
anim.setSpeed(speed);
|
|
110
|
+
|
|
111
|
+
// Set initial progress if provided
|
|
112
|
+
if (progress !== undefined) {
|
|
113
|
+
const frame = progress * anim.totalFrames;
|
|
114
|
+
anim.goToAndStop(frame, true);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Event handlers
|
|
118
|
+
anim.addEventListener('DOMLoaded', () => {
|
|
119
|
+
setIsLoaded(true);
|
|
120
|
+
onLoad?.();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
anim.addEventListener('complete', () => {
|
|
124
|
+
onComplete?.();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
anim.addEventListener('loopComplete', () => {
|
|
128
|
+
// For loop counts, onComplete is called when all loops finish
|
|
129
|
+
if (typeof loop === 'number') {
|
|
130
|
+
// lottie-web handles loop count internally
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
anim.addEventListener('enterFrame', (e: any) => {
|
|
135
|
+
onAnimationUpdate?.(e.currentTime);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
anim.addEventListener('data_failed', () => {
|
|
139
|
+
onError?.(new Error('Failed to load animation data'));
|
|
140
|
+
});
|
|
141
|
+
} catch (error) {
|
|
142
|
+
onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
loadAnimation();
|
|
147
|
+
|
|
148
|
+
return () => {
|
|
149
|
+
if (animationRef.current) {
|
|
150
|
+
animationRef.current.destroy();
|
|
151
|
+
animationRef.current = null;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}, [source, visible]);
|
|
155
|
+
|
|
156
|
+
// Update loop when it changes
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (animationRef.current) {
|
|
159
|
+
animationRef.current.loop = loopValue;
|
|
160
|
+
}
|
|
161
|
+
}, [loopValue]);
|
|
162
|
+
|
|
163
|
+
// Update speed when it changes
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (animationRef.current) {
|
|
166
|
+
animationRef.current.setSpeed(speed);
|
|
167
|
+
}
|
|
168
|
+
}, [speed]);
|
|
169
|
+
|
|
170
|
+
// Update progress when it changes
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
if (animationRef.current && progress !== undefined && isLoaded) {
|
|
173
|
+
const frame = progress * animationRef.current.totalFrames;
|
|
174
|
+
animationRef.current.goToAndStop(frame, true);
|
|
175
|
+
}
|
|
176
|
+
}, [progress, isLoaded]);
|
|
177
|
+
|
|
178
|
+
// Imperative handle for ref methods
|
|
179
|
+
useImperativeHandle(
|
|
180
|
+
ref,
|
|
181
|
+
() => ({
|
|
182
|
+
play: () => {
|
|
183
|
+
animationRef.current?.play();
|
|
184
|
+
},
|
|
185
|
+
pause: () => {
|
|
186
|
+
animationRef.current?.pause();
|
|
187
|
+
},
|
|
188
|
+
stop: () => {
|
|
189
|
+
animationRef.current?.stop();
|
|
190
|
+
},
|
|
191
|
+
reset: () => {
|
|
192
|
+
animationRef.current?.goToAndStop(0, true);
|
|
193
|
+
},
|
|
194
|
+
setProgress: (value: number) => {
|
|
195
|
+
if (animationRef.current) {
|
|
196
|
+
const frame = value * animationRef.current.totalFrames;
|
|
197
|
+
animationRef.current.goToAndStop(frame, true);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
goToAndStop: (frame: number, isFrame = true) => {
|
|
201
|
+
animationRef.current?.goToAndStop(frame, isFrame);
|
|
202
|
+
},
|
|
203
|
+
goToAndPlay: (frame: number, isFrame = true) => {
|
|
204
|
+
animationRef.current?.goToAndPlay(frame, isFrame);
|
|
205
|
+
},
|
|
206
|
+
setSpeed: (value: number) => {
|
|
207
|
+
animationRef.current?.setSpeed(value);
|
|
208
|
+
},
|
|
209
|
+
setDirection: (direction: 1 | -1) => {
|
|
210
|
+
animationRef.current?.setDirection(direction);
|
|
211
|
+
},
|
|
212
|
+
playSegments: (startFrame: number, endFrame: number, forceFlag = false) => {
|
|
213
|
+
animationRef.current?.playSegments([startFrame, endFrame], forceFlag);
|
|
214
|
+
},
|
|
215
|
+
getCurrentFrame: () => {
|
|
216
|
+
return animationRef.current?.currentFrame ?? 0;
|
|
217
|
+
},
|
|
218
|
+
getTotalFrames: () => {
|
|
219
|
+
return animationRef.current?.totalFrames ?? 0;
|
|
220
|
+
},
|
|
221
|
+
getDuration: () => {
|
|
222
|
+
if (!animationRef.current) return 0;
|
|
223
|
+
return animationRef.current.totalFrames / animationRef.current.frameRate;
|
|
224
|
+
},
|
|
225
|
+
isPlaying: () => {
|
|
226
|
+
return animationRef.current ? !animationRef.current.isPaused : false;
|
|
227
|
+
},
|
|
228
|
+
destroy: () => {
|
|
229
|
+
animationRef.current?.destroy();
|
|
230
|
+
animationRef.current = null;
|
|
231
|
+
},
|
|
232
|
+
}),
|
|
233
|
+
[]
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
if (!visible) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Convert ViewStyle to CSSProperties for web
|
|
241
|
+
const containerStyle: React.CSSProperties = {
|
|
242
|
+
width: '100%',
|
|
243
|
+
height: '100%',
|
|
244
|
+
...(style as React.CSSProperties),
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return <div ref={containerRef} style={containerStyle} data-testid={testID} />;
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
Lottie.displayName = 'Lottie';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @idealyst/lottie - Native exports
|
|
3
|
+
*
|
|
4
|
+
* Cross-platform Lottie animation component for React and React Native.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { Lottie, LottieRef } from '@idealyst/lottie';
|
|
9
|
+
*
|
|
10
|
+
* // With require (recommended for native)
|
|
11
|
+
* <Lottie source={require('./animation.json')} autoPlay loop />
|
|
12
|
+
*
|
|
13
|
+
* // With imported JSON
|
|
14
|
+
* import animationData from './animation.json';
|
|
15
|
+
* <Lottie source={animationData} autoPlay />
|
|
16
|
+
*
|
|
17
|
+
* // With URL
|
|
18
|
+
* <Lottie source={{ uri: 'https://example.com/animation.json' }} autoPlay />
|
|
19
|
+
*
|
|
20
|
+
* // With ref for imperative control
|
|
21
|
+
* const lottieRef = useRef<LottieRef>(null);
|
|
22
|
+
*
|
|
23
|
+
* <Lottie
|
|
24
|
+
* ref={lottieRef}
|
|
25
|
+
* source={require('./animation.json')}
|
|
26
|
+
* onComplete={() => console.log('done')}
|
|
27
|
+
* />
|
|
28
|
+
*
|
|
29
|
+
* // Control playback
|
|
30
|
+
* lottieRef.current?.play();
|
|
31
|
+
* lottieRef.current?.pause();
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
// Type exports
|
|
36
|
+
export type {
|
|
37
|
+
LottieProps,
|
|
38
|
+
LottieRef,
|
|
39
|
+
LottieJSON,
|
|
40
|
+
LottieSource,
|
|
41
|
+
AnimationSpeed,
|
|
42
|
+
LoopConfig,
|
|
43
|
+
ResizeMode,
|
|
44
|
+
AnimationDirection,
|
|
45
|
+
} from './types';
|
|
46
|
+
|
|
47
|
+
// Component export
|
|
48
|
+
export { Lottie } from './Lottie.native';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @idealyst/lottie - Web exports
|
|
3
|
+
*
|
|
4
|
+
* Cross-platform Lottie animation component for React and React Native.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { Lottie, LottieRef } from '@idealyst/lottie';
|
|
9
|
+
*
|
|
10
|
+
* // Basic usage
|
|
11
|
+
* <Lottie source="https://example.com/animation.json" autoPlay loop />
|
|
12
|
+
*
|
|
13
|
+
* // With imported JSON
|
|
14
|
+
* import animationData from './animation.json';
|
|
15
|
+
* <Lottie source={animationData} autoPlay />
|
|
16
|
+
*
|
|
17
|
+
* // With ref for imperative control
|
|
18
|
+
* const lottieRef = useRef<LottieRef>(null);
|
|
19
|
+
*
|
|
20
|
+
* <Lottie
|
|
21
|
+
* ref={lottieRef}
|
|
22
|
+
* source={animationData}
|
|
23
|
+
* onComplete={() => console.log('done')}
|
|
24
|
+
* />
|
|
25
|
+
*
|
|
26
|
+
* // Control playback
|
|
27
|
+
* lottieRef.current?.play();
|
|
28
|
+
* lottieRef.current?.pause();
|
|
29
|
+
* lottieRef.current?.setProgress(0.5);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
// Type exports
|
|
34
|
+
export type {
|
|
35
|
+
LottieProps,
|
|
36
|
+
LottieRef,
|
|
37
|
+
LottieJSON,
|
|
38
|
+
LottieSource,
|
|
39
|
+
AnimationSpeed,
|
|
40
|
+
LoopConfig,
|
|
41
|
+
ResizeMode,
|
|
42
|
+
AnimationDirection,
|
|
43
|
+
} from './types';
|
|
44
|
+
|
|
45
|
+
// Component export
|
|
46
|
+
export { Lottie } from './Lottie';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for @idealyst/lottie
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ViewStyle } from 'react-native';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Lottie animation JSON structure (simplified type)
|
|
9
|
+
*/
|
|
10
|
+
export interface LottieJSON {
|
|
11
|
+
v: string; // version
|
|
12
|
+
fr: number; // frame rate
|
|
13
|
+
ip: number; // in point
|
|
14
|
+
op: number; // out point
|
|
15
|
+
w: number; // width
|
|
16
|
+
h: number; // height
|
|
17
|
+
nm?: string; // name
|
|
18
|
+
ddd?: number; // 3D
|
|
19
|
+
assets?: any[];
|
|
20
|
+
layers: any[];
|
|
21
|
+
markers?: any[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Animation source - can be a URL, imported JSON, or require()
|
|
26
|
+
*/
|
|
27
|
+
export type LottieSource = string | LottieJSON | { uri: string };
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Animation speed multiplier
|
|
31
|
+
*/
|
|
32
|
+
export type AnimationSpeed = number;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Loop configuration
|
|
36
|
+
*/
|
|
37
|
+
export type LoopConfig = boolean | number;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resize mode for the animation
|
|
41
|
+
*/
|
|
42
|
+
export type ResizeMode = 'cover' | 'contain' | 'center';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Direction of animation playback
|
|
46
|
+
*/
|
|
47
|
+
export type AnimationDirection = 1 | -1;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Props for the Lottie component
|
|
51
|
+
*/
|
|
52
|
+
export interface LottieProps {
|
|
53
|
+
/**
|
|
54
|
+
* The animation source. Can be:
|
|
55
|
+
* - A URL string to a .json file
|
|
56
|
+
* - An imported JSON object
|
|
57
|
+
* - A require() statement (native only)
|
|
58
|
+
* - An object with { uri: string }
|
|
59
|
+
*/
|
|
60
|
+
source: LottieSource;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether to start playing immediately on mount.
|
|
64
|
+
* @default true
|
|
65
|
+
*/
|
|
66
|
+
autoPlay?: boolean;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Whether to loop the animation.
|
|
70
|
+
* - true: loop infinitely
|
|
71
|
+
* - false: play once
|
|
72
|
+
* - number: loop that many times
|
|
73
|
+
* @default false
|
|
74
|
+
*/
|
|
75
|
+
loop?: LoopConfig;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Playback speed multiplier.
|
|
79
|
+
* - 1 = normal speed
|
|
80
|
+
* - 2 = double speed
|
|
81
|
+
* - 0.5 = half speed
|
|
82
|
+
* - negative values play in reverse
|
|
83
|
+
* @default 1
|
|
84
|
+
*/
|
|
85
|
+
speed?: AnimationSpeed;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Style to apply to the container view.
|
|
89
|
+
*/
|
|
90
|
+
style?: ViewStyle;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* How the animation should be resized to fit its container.
|
|
94
|
+
* @default 'contain'
|
|
95
|
+
*/
|
|
96
|
+
resizeMode?: ResizeMode;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Initial progress value (0-1).
|
|
100
|
+
* Useful for showing a specific frame on mount.
|
|
101
|
+
*/
|
|
102
|
+
progress?: number;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Callback when the animation completes a loop or finishes.
|
|
106
|
+
*/
|
|
107
|
+
onComplete?: () => void;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Callback when the animation is loaded and ready to play.
|
|
111
|
+
*/
|
|
112
|
+
onLoad?: () => void;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Callback when there's an error loading the animation.
|
|
116
|
+
*/
|
|
117
|
+
onError?: (error: Error) => void;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Callback for animation frame updates.
|
|
121
|
+
* Called with the current frame number.
|
|
122
|
+
*/
|
|
123
|
+
onAnimationUpdate?: (frame: number) => void;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Whether the component should render.
|
|
127
|
+
* Useful for conditional rendering without unmounting.
|
|
128
|
+
* @default true
|
|
129
|
+
*/
|
|
130
|
+
visible?: boolean;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Test ID for testing frameworks.
|
|
134
|
+
*/
|
|
135
|
+
testID?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Ref methods available on the Lottie component
|
|
140
|
+
*/
|
|
141
|
+
export interface LottieRef {
|
|
142
|
+
/**
|
|
143
|
+
* Start playing the animation from the current position.
|
|
144
|
+
*/
|
|
145
|
+
play: () => void;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Pause the animation at the current position.
|
|
149
|
+
*/
|
|
150
|
+
pause: () => void;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Stop the animation and reset to the beginning.
|
|
154
|
+
*/
|
|
155
|
+
stop: () => void;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Reset the animation to the beginning without stopping.
|
|
159
|
+
*/
|
|
160
|
+
reset: () => void;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Set the animation progress (0-1).
|
|
164
|
+
* @param progress - Value between 0 and 1
|
|
165
|
+
*/
|
|
166
|
+
setProgress: (progress: number) => void;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Go to a specific frame.
|
|
170
|
+
* @param frame - Frame number
|
|
171
|
+
* @param isFrame - Whether the value is a frame number (true) or time in seconds (false)
|
|
172
|
+
*/
|
|
173
|
+
goToAndStop: (frame: number, isFrame?: boolean) => void;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Go to a specific frame and start playing.
|
|
177
|
+
* @param frame - Frame number
|
|
178
|
+
* @param isFrame - Whether the value is a frame number (true) or time in seconds (false)
|
|
179
|
+
*/
|
|
180
|
+
goToAndPlay: (frame: number, isFrame?: boolean) => void;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Set the playback speed.
|
|
184
|
+
* @param speed - Speed multiplier
|
|
185
|
+
*/
|
|
186
|
+
setSpeed: (speed: number) => void;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Set the playback direction.
|
|
190
|
+
* @param direction - 1 for forward, -1 for reverse
|
|
191
|
+
*/
|
|
192
|
+
setDirection: (direction: AnimationDirection) => void;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Play a specific segment of the animation.
|
|
196
|
+
* @param startFrame - Start frame
|
|
197
|
+
* @param endFrame - End frame
|
|
198
|
+
* @param forceFlag - Whether to force the segment (native only)
|
|
199
|
+
*/
|
|
200
|
+
playSegments: (startFrame: number, endFrame: number, forceFlag?: boolean) => void;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get the current frame number.
|
|
204
|
+
*/
|
|
205
|
+
getCurrentFrame: () => number;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get the total number of frames.
|
|
209
|
+
*/
|
|
210
|
+
getTotalFrames: () => number;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get the animation duration in seconds.
|
|
214
|
+
*/
|
|
215
|
+
getDuration: () => number;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Check if the animation is currently playing.
|
|
219
|
+
*/
|
|
220
|
+
isPlaying: () => boolean;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Destroy the animation instance and clean up resources.
|
|
224
|
+
*/
|
|
225
|
+
destroy: () => void;
|
|
226
|
+
}
|