r3f-motion 1.0.0 → 1.0.1
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/dist/cjs/index.js +103 -24
- package/dist/es/render/events/index.mjs +49 -0
- package/dist/es/render/motion.mjs +54 -23
- package/dist/es/render/use-render.mjs +5 -3
- package/dist/index.d.ts +4 -1
- package/package.json +3 -5
package/dist/cjs/index.js
CHANGED
|
@@ -9,14 +9,16 @@ var fiber = require('@react-three/fiber');
|
|
|
9
9
|
var three = require('three');
|
|
10
10
|
|
|
11
11
|
const useRender = (Component, props, forwardedRef, instanceRef, initialValues) => {
|
|
12
|
+
const initialValuesAppliedRef = react.useRef(false);
|
|
12
13
|
/**
|
|
13
14
|
* Create a callback ref that captures the Three.js instance
|
|
14
15
|
*/
|
|
15
16
|
const callbackRef = react.useCallback((instance) => {
|
|
16
17
|
if (!instance)
|
|
17
18
|
return;
|
|
18
|
-
// Apply initial values immediately to prevent FOUC
|
|
19
|
-
if (initialValues) {
|
|
19
|
+
// Apply initial values immediately to prevent FOUC - but only once
|
|
20
|
+
if (initialValues && !initialValuesAppliedRef.current) {
|
|
21
|
+
initialValuesAppliedRef.current = true;
|
|
20
22
|
// Property mapping configuration
|
|
21
23
|
const propertyMap = {
|
|
22
24
|
x: (val) => instance.position && (instance.position.x = val),
|
|
@@ -225,17 +227,66 @@ function useTap(isStatic, props, options) {
|
|
|
225
227
|
};
|
|
226
228
|
}
|
|
227
229
|
|
|
230
|
+
function createAnimationState() {
|
|
231
|
+
return {
|
|
232
|
+
hasStarted: false,
|
|
233
|
+
completedCount: 0,
|
|
234
|
+
totalAnimations: [],
|
|
235
|
+
latestValues: {},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function createCallbackOptions(baseOpts, callbacks, state, variant, propertyKey) {
|
|
239
|
+
if (!callbacks)
|
|
240
|
+
return baseOpts;
|
|
241
|
+
const { onAnimationUpdate, onAnimationStart, onAnimationComplete } = callbacks;
|
|
242
|
+
const variantString = typeof variant === "string" ? variant : undefined;
|
|
243
|
+
return Object.assign(Object.assign({}, baseOpts), { onUpdate: onAnimationUpdate
|
|
244
|
+
? (latest) => {
|
|
245
|
+
// Fire onAnimationStart once on first update
|
|
246
|
+
if (!state.hasStarted && onAnimationStart) {
|
|
247
|
+
state.hasStarted = true;
|
|
248
|
+
onAnimationStart(variantString);
|
|
249
|
+
}
|
|
250
|
+
// Accumulate values by property key
|
|
251
|
+
if (propertyKey) {
|
|
252
|
+
state.latestValues[propertyKey] = latest;
|
|
253
|
+
}
|
|
254
|
+
// Pass accumulated values object to user callback
|
|
255
|
+
onAnimationUpdate(state.latestValues, variantString);
|
|
256
|
+
}
|
|
257
|
+
: onAnimationStart
|
|
258
|
+
? () => {
|
|
259
|
+
// If only onAnimationStart is provided, still fire it
|
|
260
|
+
if (!state.hasStarted) {
|
|
261
|
+
state.hasStarted = true;
|
|
262
|
+
onAnimationStart(variantString);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
: undefined, onComplete: () => {
|
|
266
|
+
state.completedCount++;
|
|
267
|
+
if (state.completedCount === state.totalAnimations.length &&
|
|
268
|
+
onAnimationComplete) {
|
|
269
|
+
onAnimationComplete(variantString);
|
|
270
|
+
}
|
|
271
|
+
} });
|
|
272
|
+
}
|
|
273
|
+
function registerAnimation(state) {
|
|
274
|
+
state.totalAnimations.push(1);
|
|
275
|
+
}
|
|
276
|
+
|
|
228
277
|
const MotionContext = react.createContext(null);
|
|
229
278
|
function custom(Component) {
|
|
230
279
|
const MotionComponent = react.forwardRef((props, ref) => {
|
|
231
280
|
const instanceRef = react.useRef(null);
|
|
232
281
|
const animationRef = react.useRef(null);
|
|
233
282
|
const childIndexCounterRef = react.useRef(0);
|
|
283
|
+
const animStateRef = react.useRef(createAnimationState());
|
|
284
|
+
const callbacksRef = react.useRef(undefined);
|
|
234
285
|
const parentContext = react.useContext(MotionContext);
|
|
235
286
|
const childIndex = react.useMemo(() => { var _a, _b; return (_b = (_a = parentContext === null || parentContext === void 0 ? void 0 : parentContext.getNextChildIndex) === null || _a === void 0 ? void 0 : _a.call(parentContext)) !== null && _b !== void 0 ? _b : 0; },
|
|
236
287
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
237
288
|
[]);
|
|
238
|
-
const _a = props, { initial: initialProp, animate: animateProp, transition, variants, custom, inherit = true, children } = _a, restProps = tslib.__rest(_a, ["initial", "animate", "transition", "variants", "custom", "inherit", "children"]);
|
|
289
|
+
const _a = props, { initial: initialProp, animate: animateProp, transition, variants, custom, inherit = true, children, onAnimationUpdate, onAnimationStart, onAnimationComplete } = _a, restProps = tslib.__rest(_a, ["initial", "animate", "transition", "variants", "custom", "inherit", "children", "onAnimationUpdate", "onAnimationStart", "onAnimationComplete"]);
|
|
239
290
|
const initial = initialProp !== undefined
|
|
240
291
|
? initialProp
|
|
241
292
|
: inherit && (parentContext === null || parentContext === void 0 ? void 0 : parentContext.initial);
|
|
@@ -244,6 +295,23 @@ function custom(Component) {
|
|
|
244
295
|
: inherit && (parentContext === null || parentContext === void 0 ? void 0 : parentContext.animate);
|
|
245
296
|
const effectiveVariants = variants || (inherit ? parentContext === null || parentContext === void 0 ? void 0 : parentContext.variants : undefined);
|
|
246
297
|
const effectiveCustom = custom !== undefined ? custom : parentContext === null || parentContext === void 0 ? void 0 : parentContext.custom;
|
|
298
|
+
// Update callbacks ref on every render
|
|
299
|
+
if (onAnimationUpdate || onAnimationStart || onAnimationComplete) {
|
|
300
|
+
const typedCallbacks = {};
|
|
301
|
+
if (onAnimationUpdate) {
|
|
302
|
+
typedCallbacks.onAnimationUpdate = onAnimationUpdate;
|
|
303
|
+
}
|
|
304
|
+
if (onAnimationStart) {
|
|
305
|
+
typedCallbacks.onAnimationStart = onAnimationStart;
|
|
306
|
+
}
|
|
307
|
+
if (onAnimationComplete) {
|
|
308
|
+
typedCallbacks.onAnimationComplete = onAnimationComplete;
|
|
309
|
+
}
|
|
310
|
+
callbacksRef.current = typedCallbacks;
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
callbacksRef.current = undefined;
|
|
314
|
+
}
|
|
247
315
|
const resolveVariant = react.useCallback((variantKey) => {
|
|
248
316
|
if (!effectiveVariants || !variantKey)
|
|
249
317
|
return null;
|
|
@@ -283,12 +351,19 @@ function custom(Component) {
|
|
|
283
351
|
rotateZ: capturedState.rotation.z,
|
|
284
352
|
scale: capturedState.scale.x,
|
|
285
353
|
}), []);
|
|
286
|
-
const animateToTarget = react.useCallback((targetValues, options) => {
|
|
354
|
+
const animateToTarget = react.useCallback((targetValues, options, useCallbacks = false) => {
|
|
287
355
|
var _a;
|
|
288
356
|
const instance = instanceRef.current;
|
|
289
357
|
if (!instance || !targetValues)
|
|
290
358
|
return;
|
|
291
359
|
(_a = animationRef.current) === null || _a === void 0 ? void 0 : _a.stop();
|
|
360
|
+
// Reset animation state when using callbacks
|
|
361
|
+
if (useCallbacks) {
|
|
362
|
+
animStateRef.current = createAnimationState();
|
|
363
|
+
}
|
|
364
|
+
const animState = animStateRef.current;
|
|
365
|
+
const callbacks = useCallbacks ? callbacksRef.current : undefined;
|
|
366
|
+
const animateVariant = animate;
|
|
292
367
|
const convertOptions = (opts) => {
|
|
293
368
|
if (!opts)
|
|
294
369
|
return { type: "tween" };
|
|
@@ -330,42 +405,52 @@ function custom(Component) {
|
|
|
330
405
|
scaleZ: { target: instance.scale, prop: "z" },
|
|
331
406
|
};
|
|
332
407
|
const animations = [];
|
|
333
|
-
|
|
408
|
+
// Helper to create animation with optional callbacks
|
|
409
|
+
const createAnimation = (target, props, opts, propertyKey) => {
|
|
410
|
+
const animOpts = callbacks
|
|
411
|
+
? createCallbackOptions(opts, callbacks, animState, animateVariant, propertyKey)
|
|
412
|
+
: opts;
|
|
413
|
+
animations.push(motion$1.animate(target, props, animOpts));
|
|
414
|
+
if (callbacks) {
|
|
415
|
+
registerAnimation(animState);
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
const animateColor = (target, value, opts, key) => {
|
|
334
419
|
const ColorConstructor = target.constructor;
|
|
335
420
|
const tempColor = new ColorConstructor(value);
|
|
336
|
-
["r", "g", "b"].forEach((channel) =>
|
|
421
|
+
["r", "g", "b"].forEach((channel) => createAnimation(target, { [channel]: tempColor[channel] }, opts, key));
|
|
337
422
|
};
|
|
338
423
|
Object.entries(targetValues).forEach(([key, value]) => {
|
|
339
424
|
const opts = getPropertyOpts(key);
|
|
340
425
|
const mapping = transformMap[key];
|
|
341
426
|
if (mapping === null || mapping === void 0 ? void 0 : mapping.target) {
|
|
342
427
|
if (mapping.multi) {
|
|
343
|
-
["x", "y", "z"].forEach((axis) =>
|
|
428
|
+
["x", "y", "z"].forEach((axis) => createAnimation(mapping.target, { [axis]: value }, opts, key));
|
|
344
429
|
}
|
|
345
430
|
else {
|
|
346
|
-
|
|
431
|
+
createAnimation(mapping.target, { [mapping.prop]: value }, opts, key);
|
|
347
432
|
}
|
|
348
433
|
}
|
|
349
434
|
else if (key === "color" && instance.color) {
|
|
350
|
-
animateColor(instance.color, value, opts);
|
|
435
|
+
animateColor(instance.color, value, opts, key);
|
|
351
436
|
}
|
|
352
437
|
else if (key === "emissive" && instance.emissive) {
|
|
353
|
-
animateColor(instance.emissive, value, opts);
|
|
438
|
+
animateColor(instance.emissive, value, opts, key);
|
|
354
439
|
}
|
|
355
440
|
else if (key === "opacity" && instance.opacity !== undefined) {
|
|
356
|
-
|
|
441
|
+
createAnimation(instance, { opacity: value }, opts, key);
|
|
357
442
|
}
|
|
358
443
|
else if (key === "emissiveIntensity" &&
|
|
359
444
|
instance.emissiveIntensity !== undefined) {
|
|
360
|
-
|
|
445
|
+
createAnimation(instance, { emissiveIntensity: value }, opts, key);
|
|
361
446
|
}
|
|
362
447
|
else if (key === "roughness" &&
|
|
363
448
|
instance.roughness !== undefined) {
|
|
364
|
-
|
|
449
|
+
createAnimation(instance, { roughness: value }, opts, key);
|
|
365
450
|
}
|
|
366
451
|
else if (key === "metalness" &&
|
|
367
452
|
instance.metalness !== undefined) {
|
|
368
|
-
|
|
453
|
+
createAnimation(instance, { metalness: value }, opts, key);
|
|
369
454
|
}
|
|
370
455
|
});
|
|
371
456
|
animationRef.current = {
|
|
@@ -388,16 +473,10 @@ function custom(Component) {
|
|
|
388
473
|
childIndex * (parentTrans.staggerChildren || 0);
|
|
389
474
|
effectiveTransition = Object.assign(Object.assign({}, effectiveTransition), { delay: ((effectiveTransition === null || effectiveTransition === void 0 ? void 0 : effectiveTransition.delay) || 0) + orchestrationDelay });
|
|
390
475
|
}
|
|
391
|
-
animateToTarget(targetValues, effectiveTransition);
|
|
476
|
+
animateToTarget(targetValues, effectiveTransition, true);
|
|
392
477
|
return () => { var _a; return (_a = animationRef.current) === null || _a === void 0 ? void 0 : _a.stop(); };
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
transition,
|
|
396
|
-
resolveVariant,
|
|
397
|
-
animateToTarget,
|
|
398
|
-
childIndex,
|
|
399
|
-
parentContext,
|
|
400
|
-
]);
|
|
478
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
479
|
+
}, [animate]);
|
|
401
480
|
const gestureProps = {
|
|
402
481
|
captureInstanceState,
|
|
403
482
|
buildTargetFromState,
|
|
@@ -427,7 +506,7 @@ function custom(Component) {
|
|
|
427
506
|
return element;
|
|
428
507
|
});
|
|
429
508
|
MotionComponent.displayName = `Motion(${Component})`;
|
|
430
|
-
return MotionComponent;
|
|
509
|
+
return react.memo(MotionComponent);
|
|
431
510
|
}
|
|
432
511
|
const componentCache = new Map();
|
|
433
512
|
const motion = new Proxy(custom, {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
function createAnimationState() {
|
|
3
|
+
return {
|
|
4
|
+
hasStarted: false,
|
|
5
|
+
completedCount: 0,
|
|
6
|
+
totalAnimations: [],
|
|
7
|
+
latestValues: {},
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function createCallbackOptions(baseOpts, callbacks, state, variant, propertyKey) {
|
|
11
|
+
if (!callbacks)
|
|
12
|
+
return baseOpts;
|
|
13
|
+
const { onAnimationUpdate, onAnimationStart, onAnimationComplete } = callbacks;
|
|
14
|
+
const variantString = typeof variant === "string" ? variant : undefined;
|
|
15
|
+
return Object.assign(Object.assign({}, baseOpts), { onUpdate: onAnimationUpdate
|
|
16
|
+
? (latest) => {
|
|
17
|
+
// Fire onAnimationStart once on first update
|
|
18
|
+
if (!state.hasStarted && onAnimationStart) {
|
|
19
|
+
state.hasStarted = true;
|
|
20
|
+
onAnimationStart(variantString);
|
|
21
|
+
}
|
|
22
|
+
// Accumulate values by property key
|
|
23
|
+
if (propertyKey) {
|
|
24
|
+
state.latestValues[propertyKey] = latest;
|
|
25
|
+
}
|
|
26
|
+
// Pass accumulated values object to user callback
|
|
27
|
+
onAnimationUpdate(state.latestValues, variantString);
|
|
28
|
+
}
|
|
29
|
+
: onAnimationStart
|
|
30
|
+
? () => {
|
|
31
|
+
// If only onAnimationStart is provided, still fire it
|
|
32
|
+
if (!state.hasStarted) {
|
|
33
|
+
state.hasStarted = true;
|
|
34
|
+
onAnimationStart(variantString);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
: undefined, onComplete: () => {
|
|
38
|
+
state.completedCount++;
|
|
39
|
+
if (state.completedCount === state.totalAnimations.length &&
|
|
40
|
+
onAnimationComplete) {
|
|
41
|
+
onAnimationComplete(variantString);
|
|
42
|
+
}
|
|
43
|
+
} });
|
|
44
|
+
}
|
|
45
|
+
function registerAnimation(state) {
|
|
46
|
+
state.totalAnimations.push(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { createAnimationState, createCallbackOptions, registerAnimation };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { __rest } from 'tslib';
|
|
3
|
-
import { createContext, forwardRef, useRef, useContext, useMemo, useCallback, useEffect, createElement } from 'react';
|
|
3
|
+
import { createContext, forwardRef, useRef, useContext, useMemo, useCallback, useEffect, createElement, memo } from 'react';
|
|
4
4
|
import { animate } from 'motion';
|
|
5
5
|
import { useRender } from './use-render.mjs';
|
|
6
6
|
import { useHover } from './gestures/use-hover.mjs';
|
|
7
7
|
import { useTap } from './gestures/use-tap.mjs';
|
|
8
|
+
import { createAnimationState, createCallbackOptions, registerAnimation } from './events/index.mjs';
|
|
8
9
|
|
|
9
10
|
const MotionContext = createContext(null);
|
|
10
11
|
function custom(Component) {
|
|
@@ -12,11 +13,13 @@ function custom(Component) {
|
|
|
12
13
|
const instanceRef = useRef(null);
|
|
13
14
|
const animationRef = useRef(null);
|
|
14
15
|
const childIndexCounterRef = useRef(0);
|
|
16
|
+
const animStateRef = useRef(createAnimationState());
|
|
17
|
+
const callbacksRef = useRef(undefined);
|
|
15
18
|
const parentContext = useContext(MotionContext);
|
|
16
19
|
const childIndex = useMemo(() => { var _a, _b; return (_b = (_a = parentContext === null || parentContext === void 0 ? void 0 : parentContext.getNextChildIndex) === null || _a === void 0 ? void 0 : _a.call(parentContext)) !== null && _b !== void 0 ? _b : 0; },
|
|
17
20
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
18
21
|
[]);
|
|
19
|
-
const _a = props, { initial: initialProp, animate: animateProp, transition, variants, custom, inherit = true, children } = _a, restProps = __rest(_a, ["initial", "animate", "transition", "variants", "custom", "inherit", "children"]);
|
|
22
|
+
const _a = props, { initial: initialProp, animate: animateProp, transition, variants, custom, inherit = true, children, onAnimationUpdate, onAnimationStart, onAnimationComplete } = _a, restProps = __rest(_a, ["initial", "animate", "transition", "variants", "custom", "inherit", "children", "onAnimationUpdate", "onAnimationStart", "onAnimationComplete"]);
|
|
20
23
|
const initial = initialProp !== undefined
|
|
21
24
|
? initialProp
|
|
22
25
|
: inherit && (parentContext === null || parentContext === void 0 ? void 0 : parentContext.initial);
|
|
@@ -25,6 +28,23 @@ function custom(Component) {
|
|
|
25
28
|
: inherit && (parentContext === null || parentContext === void 0 ? void 0 : parentContext.animate);
|
|
26
29
|
const effectiveVariants = variants || (inherit ? parentContext === null || parentContext === void 0 ? void 0 : parentContext.variants : undefined);
|
|
27
30
|
const effectiveCustom = custom !== undefined ? custom : parentContext === null || parentContext === void 0 ? void 0 : parentContext.custom;
|
|
31
|
+
// Update callbacks ref on every render
|
|
32
|
+
if (onAnimationUpdate || onAnimationStart || onAnimationComplete) {
|
|
33
|
+
const typedCallbacks = {};
|
|
34
|
+
if (onAnimationUpdate) {
|
|
35
|
+
typedCallbacks.onAnimationUpdate = onAnimationUpdate;
|
|
36
|
+
}
|
|
37
|
+
if (onAnimationStart) {
|
|
38
|
+
typedCallbacks.onAnimationStart = onAnimationStart;
|
|
39
|
+
}
|
|
40
|
+
if (onAnimationComplete) {
|
|
41
|
+
typedCallbacks.onAnimationComplete = onAnimationComplete;
|
|
42
|
+
}
|
|
43
|
+
callbacksRef.current = typedCallbacks;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
callbacksRef.current = undefined;
|
|
47
|
+
}
|
|
28
48
|
const resolveVariant = useCallback((variantKey) => {
|
|
29
49
|
if (!effectiveVariants || !variantKey)
|
|
30
50
|
return null;
|
|
@@ -64,12 +84,19 @@ function custom(Component) {
|
|
|
64
84
|
rotateZ: capturedState.rotation.z,
|
|
65
85
|
scale: capturedState.scale.x,
|
|
66
86
|
}), []);
|
|
67
|
-
const animateToTarget = useCallback((targetValues, options) => {
|
|
87
|
+
const animateToTarget = useCallback((targetValues, options, useCallbacks = false) => {
|
|
68
88
|
var _a;
|
|
69
89
|
const instance = instanceRef.current;
|
|
70
90
|
if (!instance || !targetValues)
|
|
71
91
|
return;
|
|
72
92
|
(_a = animationRef.current) === null || _a === void 0 ? void 0 : _a.stop();
|
|
93
|
+
// Reset animation state when using callbacks
|
|
94
|
+
if (useCallbacks) {
|
|
95
|
+
animStateRef.current = createAnimationState();
|
|
96
|
+
}
|
|
97
|
+
const animState = animStateRef.current;
|
|
98
|
+
const callbacks = useCallbacks ? callbacksRef.current : undefined;
|
|
99
|
+
const animateVariant = animate$1;
|
|
73
100
|
const convertOptions = (opts) => {
|
|
74
101
|
if (!opts)
|
|
75
102
|
return { type: "tween" };
|
|
@@ -111,42 +138,52 @@ function custom(Component) {
|
|
|
111
138
|
scaleZ: { target: instance.scale, prop: "z" },
|
|
112
139
|
};
|
|
113
140
|
const animations = [];
|
|
114
|
-
|
|
141
|
+
// Helper to create animation with optional callbacks
|
|
142
|
+
const createAnimation = (target, props, opts, propertyKey) => {
|
|
143
|
+
const animOpts = callbacks
|
|
144
|
+
? createCallbackOptions(opts, callbacks, animState, animateVariant, propertyKey)
|
|
145
|
+
: opts;
|
|
146
|
+
animations.push(animate(target, props, animOpts));
|
|
147
|
+
if (callbacks) {
|
|
148
|
+
registerAnimation(animState);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const animateColor = (target, value, opts, key) => {
|
|
115
152
|
const ColorConstructor = target.constructor;
|
|
116
153
|
const tempColor = new ColorConstructor(value);
|
|
117
|
-
["r", "g", "b"].forEach((channel) =>
|
|
154
|
+
["r", "g", "b"].forEach((channel) => createAnimation(target, { [channel]: tempColor[channel] }, opts, key));
|
|
118
155
|
};
|
|
119
156
|
Object.entries(targetValues).forEach(([key, value]) => {
|
|
120
157
|
const opts = getPropertyOpts(key);
|
|
121
158
|
const mapping = transformMap[key];
|
|
122
159
|
if (mapping === null || mapping === void 0 ? void 0 : mapping.target) {
|
|
123
160
|
if (mapping.multi) {
|
|
124
|
-
["x", "y", "z"].forEach((axis) =>
|
|
161
|
+
["x", "y", "z"].forEach((axis) => createAnimation(mapping.target, { [axis]: value }, opts, key));
|
|
125
162
|
}
|
|
126
163
|
else {
|
|
127
|
-
|
|
164
|
+
createAnimation(mapping.target, { [mapping.prop]: value }, opts, key);
|
|
128
165
|
}
|
|
129
166
|
}
|
|
130
167
|
else if (key === "color" && instance.color) {
|
|
131
|
-
animateColor(instance.color, value, opts);
|
|
168
|
+
animateColor(instance.color, value, opts, key);
|
|
132
169
|
}
|
|
133
170
|
else if (key === "emissive" && instance.emissive) {
|
|
134
|
-
animateColor(instance.emissive, value, opts);
|
|
171
|
+
animateColor(instance.emissive, value, opts, key);
|
|
135
172
|
}
|
|
136
173
|
else if (key === "opacity" && instance.opacity !== undefined) {
|
|
137
|
-
|
|
174
|
+
createAnimation(instance, { opacity: value }, opts, key);
|
|
138
175
|
}
|
|
139
176
|
else if (key === "emissiveIntensity" &&
|
|
140
177
|
instance.emissiveIntensity !== undefined) {
|
|
141
|
-
|
|
178
|
+
createAnimation(instance, { emissiveIntensity: value }, opts, key);
|
|
142
179
|
}
|
|
143
180
|
else if (key === "roughness" &&
|
|
144
181
|
instance.roughness !== undefined) {
|
|
145
|
-
|
|
182
|
+
createAnimation(instance, { roughness: value }, opts, key);
|
|
146
183
|
}
|
|
147
184
|
else if (key === "metalness" &&
|
|
148
185
|
instance.metalness !== undefined) {
|
|
149
|
-
|
|
186
|
+
createAnimation(instance, { metalness: value }, opts, key);
|
|
150
187
|
}
|
|
151
188
|
});
|
|
152
189
|
animationRef.current = {
|
|
@@ -169,16 +206,10 @@ function custom(Component) {
|
|
|
169
206
|
childIndex * (parentTrans.staggerChildren || 0);
|
|
170
207
|
effectiveTransition = Object.assign(Object.assign({}, effectiveTransition), { delay: ((effectiveTransition === null || effectiveTransition === void 0 ? void 0 : effectiveTransition.delay) || 0) + orchestrationDelay });
|
|
171
208
|
}
|
|
172
|
-
animateToTarget(targetValues, effectiveTransition);
|
|
209
|
+
animateToTarget(targetValues, effectiveTransition, true);
|
|
173
210
|
return () => { var _a; return (_a = animationRef.current) === null || _a === void 0 ? void 0 : _a.stop(); };
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
transition,
|
|
177
|
-
resolveVariant,
|
|
178
|
-
animateToTarget,
|
|
179
|
-
childIndex,
|
|
180
|
-
parentContext,
|
|
181
|
-
]);
|
|
211
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
212
|
+
}, [animate$1]);
|
|
182
213
|
const gestureProps = {
|
|
183
214
|
captureInstanceState,
|
|
184
215
|
buildTargetFromState,
|
|
@@ -208,7 +239,7 @@ function custom(Component) {
|
|
|
208
239
|
return element;
|
|
209
240
|
});
|
|
210
241
|
MotionComponent.displayName = `Motion(${Component})`;
|
|
211
|
-
return MotionComponent;
|
|
242
|
+
return memo(MotionComponent);
|
|
212
243
|
}
|
|
213
244
|
const componentCache = new Map();
|
|
214
245
|
const motion = new Proxy(custom, {
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useCallback, createElement } from 'react';
|
|
2
|
+
import { useRef, useCallback, createElement } from 'react';
|
|
3
3
|
|
|
4
4
|
const useRender = (Component, props, forwardedRef, instanceRef, initialValues) => {
|
|
5
|
+
const initialValuesAppliedRef = useRef(false);
|
|
5
6
|
/**
|
|
6
7
|
* Create a callback ref that captures the Three.js instance
|
|
7
8
|
*/
|
|
8
9
|
const callbackRef = useCallback((instance) => {
|
|
9
10
|
if (!instance)
|
|
10
11
|
return;
|
|
11
|
-
// Apply initial values immediately to prevent FOUC
|
|
12
|
-
if (initialValues) {
|
|
12
|
+
// Apply initial values immediately to prevent FOUC - but only once
|
|
13
|
+
if (initialValues && !initialValuesAppliedRef.current) {
|
|
14
|
+
initialValuesAppliedRef.current = true;
|
|
13
15
|
// Property mapping configuration
|
|
14
16
|
const propertyMap = {
|
|
15
17
|
x: (val) => instance.position && (instance.position.x = val),
|
package/dist/index.d.ts
CHANGED
|
@@ -5,9 +5,12 @@ import { MotionValue, MotionProps, ResolvedValues } from 'motion/react';
|
|
|
5
5
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
type ThreeElement = InstanceType<typeof THREE.Object3D> & Record<string, unknown>;
|
|
8
|
-
interface ThreeMotionProps extends Omit<MotionProps, "style" | "children"> {
|
|
8
|
+
interface ThreeMotionProps extends Omit<MotionProps, "style" | "children" | "onUpdate" | "onAnimationStart" | "onAnimationComplete"> {
|
|
9
9
|
[key: string]: unknown;
|
|
10
10
|
onInstanceUpdate?: ReactThreeFiber.ThreeElements["object3D"]["onUpdate"];
|
|
11
|
+
onUpdate?: (values: Record<string, unknown>, animationVariant?: string) => void;
|
|
12
|
+
onAnimationStart?: (values: Record<string, unknown>, animationVariant?: string) => void;
|
|
13
|
+
onAnimationComplete?: (values: Record<string, unknown>, animationVariant?: string) => void;
|
|
11
14
|
}
|
|
12
15
|
interface ThreeRenderState {
|
|
13
16
|
[key: string]: unknown;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "r3f-motion",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "A simple and powerful React animation library for @react-three/fiber leveraging motion.dev",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/es/index.mjs",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@react-three/fiber": "^9.5.0",
|
|
58
|
-
"react": "^19.
|
|
59
|
-
"react-dom": "^19.
|
|
58
|
+
"react": "^19.0.0",
|
|
59
|
+
"react-dom": "^19.0.0",
|
|
60
60
|
"three": "^0.182.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
@@ -67,8 +67,6 @@
|
|
|
67
67
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
68
68
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
69
69
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
70
|
-
"@storybook/addon-essentials": "^8.6.14",
|
|
71
|
-
"@storybook/addon-interactions": "^8.6.14",
|
|
72
70
|
"@storybook/react": "^10.2.0",
|
|
73
71
|
"@storybook/react-vite": "^10.2.0",
|
|
74
72
|
"@storybook/test": "^8.6.15",
|