|
1 | | -/* eslint-disable no-bitwise */ |
2 | 1 | import React, { |
3 | 2 | useCallback, |
4 | 3 | useEffect, |
@@ -40,6 +39,7 @@ import { convertErrorFromNativeToRN, XOR } from './helpers'; |
40 | 39 | import { Alignment, Fit } from './types'; |
41 | 40 | import { |
42 | 41 | getPropertyTypeString, |
| 42 | + intToRiveRGBA, |
43 | 43 | parseColor, |
44 | 44 | parsePossibleSources, |
45 | 45 | } from './utils'; |
@@ -132,153 +132,138 @@ export class RivePropertyValueEmitter { |
132 | 132 | } |
133 | 133 | } |
134 | 134 |
|
| 135 | +export function useRive(): [(node: RiveRef) => void, RiveRef | null] { |
| 136 | + const [ref, setRef] = useState<RiveRef | null>(null); |
| 137 | + const setRiveRef = useCallback<(node: RiveRef) => void>( |
| 138 | + (node) => setRef(node), |
| 139 | + [] |
| 140 | + ); |
| 141 | + return [setRiveRef, ref]; |
| 142 | +} |
| 143 | + |
135 | 144 | export function useRiveBoolean( |
136 | | - riveRef: React.RefObject<RiveRef>, |
| 145 | + riveRef: RiveRef | null, |
137 | 146 | path: string |
138 | 147 | ): [boolean | undefined, (value: boolean) => void] { |
139 | 148 | return useRivePropertyListener<boolean>(riveRef, path, PropertyType.Boolean); |
140 | 149 | } |
141 | 150 |
|
142 | 151 | export function useRiveString( |
143 | | - riveRef: React.RefObject<RiveRef>, |
| 152 | + riveRef: RiveRef | null, |
144 | 153 | path: string |
145 | 154 | ): [string | undefined, (value: string) => void] { |
146 | 155 | return useRivePropertyListener<string>(riveRef, path, PropertyType.String); |
147 | 156 | } |
148 | 157 |
|
149 | 158 | export function useRiveNumber( |
150 | | - riveRef: React.RefObject<RiveRef>, |
| 159 | + riveRef: RiveRef | null, |
151 | 160 | path: string |
152 | 161 | ): [number | undefined, (value: number) => void] { |
153 | 162 | return useRivePropertyListener<number>(riveRef, path, PropertyType.Number); |
154 | 163 | } |
155 | 164 |
|
156 | 165 | export function useRiveEnum( |
157 | | - riveRef: React.RefObject<RiveRef>, |
| 166 | + riveRef: RiveRef | null, |
158 | 167 | path: string |
159 | 168 | ): [string | undefined, (value: string) => void] { |
160 | 169 | return useRivePropertyListener<string>(riveRef, path, PropertyType.Enum); |
161 | 170 | } |
162 | 171 |
|
163 | 172 | export function useRiveColor( |
164 | | - riveRef: React.RefObject<RiveRef>, |
| 173 | + riveRef: RiveRef | null, |
165 | 174 | path: string |
166 | | -): [RiveRGBA | undefined, (value: RiveRGBA) => void] { |
167 | | - const [color, setColor] = useState<RiveRGBA | undefined>(undefined); |
168 | | - |
169 | | - // Convert integer to RiveRGBA |
170 | | - const intToRgba = useCallback((colorValue: number): RiveRGBA => { |
171 | | - const a = (colorValue >> 24) & 0xff; |
172 | | - const r = (colorValue >> 16) & 0xff; |
173 | | - const g = (colorValue >> 8) & 0xff; |
174 | | - const b = colorValue & 0xff; |
175 | | - return { r, g, b, a }; |
176 | | - }, []); |
177 | | - |
178 | | - // Listener callback to update state |
179 | | - const listenerCallback = useCallback( |
180 | | - (newValue: number) => { |
181 | | - setColor(intToRgba(newValue)); |
182 | | - }, |
183 | | - [intToRgba] |
184 | | - ); |
185 | | - |
186 | | - useEffect(() => { |
187 | | - const ref = riveRef.current; |
188 | | - if (!ref) { |
189 | | - return undefined; |
190 | | - } |
191 | | - |
192 | | - const listener = ref.internalPropertyListener(); |
193 | | - if (!listener) { |
194 | | - return undefined; |
195 | | - } |
196 | | - |
197 | | - listener.addListener<number>(path, PropertyType.Color, listenerCallback); |
198 | | - |
199 | | - return () => { |
200 | | - listener.removeListener(path, PropertyType.Color, listenerCallback); |
201 | | - }; |
202 | | - // eslint-disable-next-line react-hooks/exhaustive-deps |
203 | | - }, [path, listenerCallback]); |
204 | | - |
205 | | - const setColorPropertyValue = useCallback( |
206 | | - (newColor: RiveRGBA) => { |
207 | | - const ref = riveRef.current; |
208 | | - if (!ref) { |
209 | | - console.warn('Rive ref is not available to set color property.'); |
210 | | - return; |
211 | | - } |
212 | | - ref.setColor(path, newColor); |
213 | | - }, |
214 | | - // eslint-disable-next-line react-hooks/exhaustive-deps |
215 | | - [path] |
216 | | - ); |
217 | | - |
218 | | - return [color, setColorPropertyValue]; |
| 175 | +): [RiveRGBA | undefined, (value: RiveRGBA | string) => void] { |
| 176 | + return useRivePropertyListener<RiveRGBA>(riveRef, path, PropertyType.Color); |
219 | 177 | } |
220 | 178 |
|
221 | 179 | function useRivePropertyListener<T>( |
222 | | - riveRef: React.RefObject<RiveRef>, |
| 180 | + riveRef: RiveRef | null, |
223 | 181 | path: string, |
224 | 182 | propertyType: PropertyType |
225 | 183 | ): [T | undefined, (value: T) => void] { |
226 | 184 | const [value, setValue] = useState<T | undefined>(undefined); |
227 | 185 |
|
228 | | - // Listener callback to update state |
| 186 | + // Listener callback to update state for non-color properties |
229 | 187 | const listenerCallback = useCallback((newValue: T) => { |
230 | 188 | setValue(newValue); |
231 | 189 | }, []); |
232 | 190 |
|
233 | | - useEffect(() => { |
234 | | - const ref = riveRef.current; |
235 | | - if (!ref) { |
236 | | - return undefined; |
237 | | - } |
| 191 | + // Listener callback to update state for color properties |
| 192 | + const listenerCallbackWithColor = useCallback((newValue: number) => { |
| 193 | + const rgbaValue = intToRiveRGBA(newValue); |
| 194 | + setValue(rgbaValue as T); |
| 195 | + }, []); |
238 | 196 |
|
239 | | - const listener = ref.internalPropertyListener(); |
240 | | - if (!listener) { |
241 | | - return undefined; |
| 197 | + useEffect(() => { |
| 198 | + const listener = riveRef?.internalPropertyListener?.(); |
| 199 | + if (!listener) return () => {}; |
| 200 | + |
| 201 | + if (propertyType === PropertyType.Color) { |
| 202 | + listener.addListener<number>( |
| 203 | + path, |
| 204 | + propertyType, |
| 205 | + listenerCallbackWithColor |
| 206 | + ); |
| 207 | + return () => { |
| 208 | + listener.removeListener<number>( |
| 209 | + path, |
| 210 | + propertyType, |
| 211 | + listenerCallbackWithColor |
| 212 | + ); |
| 213 | + }; |
| 214 | + } else { |
| 215 | + listener.addListener<T>(path, propertyType, listenerCallback); |
| 216 | + return () => { |
| 217 | + listener.removeListener<T>(path, propertyType, listenerCallback); |
| 218 | + }; |
242 | 219 | } |
243 | | - |
244 | | - listener.addListener<T>(path, propertyType, listenerCallback); |
245 | | - |
246 | | - return () => { |
247 | | - listener.removeListener(path, propertyType, listenerCallback); |
248 | | - }; |
249 | | - // eslint-disable-next-line react-hooks/exhaustive-deps |
250 | | - }, [path, propertyType, listenerCallback]); |
| 220 | + }, [ |
| 221 | + riveRef, |
| 222 | + path, |
| 223 | + propertyType, |
| 224 | + listenerCallback, |
| 225 | + listenerCallbackWithColor, |
| 226 | + ]); |
251 | 227 |
|
252 | 228 | // Setter function |
253 | 229 | const setPropertyValue = useCallback( |
254 | 230 | (newValue: T) => { |
255 | | - const ref = riveRef.current; |
256 | | - if (!ref) { |
257 | | - console.warn('Rive ref is not available to set property.'); |
| 231 | + if (!riveRef) { |
| 232 | + if (__DEV__) { |
| 233 | + console.warn( |
| 234 | + `[Rive] Tried to set property "${path}" before riveRef was available.` |
| 235 | + ); |
| 236 | + } |
258 | 237 | return; |
259 | 238 | } |
260 | 239 |
|
261 | 240 | switch (propertyType) { |
262 | 241 | case PropertyType.Number: |
263 | | - ref.setNumber(path, newValue as number); |
| 242 | + riveRef.setNumber(path, newValue as number); |
264 | 243 | break; |
265 | 244 | case PropertyType.Boolean: |
266 | | - ref.setBoolean(path, newValue as boolean); |
| 245 | + riveRef.setBoolean(path, newValue as boolean); |
267 | 246 | break; |
268 | 247 | case PropertyType.String: |
269 | | - ref.setString(path, newValue as string); |
| 248 | + riveRef.setString(path, newValue as string); |
270 | 249 | break; |
271 | 250 | case PropertyType.Enum: |
272 | | - ref.setEnum(path, newValue as string); |
| 251 | + riveRef.setEnum(path, newValue as string); |
| 252 | + break; |
| 253 | + case PropertyType.Color: |
| 254 | + const parsedColor = |
| 255 | + typeof newValue === 'string' ? parseColor(newValue) : newValue; |
| 256 | + riveRef.setColor(path, parsedColor as RiveRGBA); |
273 | 257 | break; |
274 | 258 | default: |
275 | | - console.warn( |
276 | | - `Rive unsupported property type in generic listener: ${propertyType}` |
277 | | - ); |
| 259 | + if (__DEV__) { |
| 260 | + console.warn( |
| 261 | + `[Rive] Unsupported property type in generic listener: ${propertyType}` |
| 262 | + ); |
| 263 | + } |
278 | 264 | } |
279 | 265 | }, |
280 | | - // eslint-disable-next-line react-hooks/exhaustive-deps |
281 | | - [path, propertyType] |
| 266 | + [riveRef, path, propertyType] |
282 | 267 | ); |
283 | 268 |
|
284 | 269 | return [value, setPropertyValue]; |
|
0 commit comments