remotion 4.0.508 → 4.0.509
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/RemotionRoot.d.ts +1 -0
- package/dist/cjs/RemotionRoot.js +2 -2
- package/dist/cjs/audio/shared-audio-tags.d.ts +2 -0
- package/dist/cjs/audio/shared-audio-tags.js +74 -5
- package/dist/cjs/audio/wait-until-actually-resumed.js +21 -8
- package/dist/cjs/internals.d.ts +5 -1
- package/dist/cjs/interpolate.js +81 -2
- package/dist/cjs/use-schema.d.ts +2 -1
- package/dist/cjs/use-schema.js +3 -3
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/index.mjs +147 -16
- package/dist/esm/no-react.mjs +607 -72
- package/dist/esm/version.mjs +1 -1
- package/package.json +2 -2
package/dist/cjs/RemotionRoot.js
CHANGED
|
@@ -13,7 +13,7 @@ const SequenceManager_js_1 = require("./SequenceManager.js");
|
|
|
13
13
|
const TimelineContext_js_1 = require("./TimelineContext.js");
|
|
14
14
|
const use_media_enabled_js_1 = require("./use-media-enabled.js");
|
|
15
15
|
const duration_state_js_1 = require("./video/duration-state.js");
|
|
16
|
-
const RemotionRootContexts = ({ children, numberOfAudioTags, logLevel, audioLatencyHint, previewSampleRate, videoEnabled, audioEnabled, frameState, }) => {
|
|
16
|
+
const RemotionRootContexts = ({ children, numberOfAudioTags, logLevel, audioLatencyHint, previewSampleRate, videoEnabled, audioEnabled, frameState, _experimentalKeepAudioContextAlive, }) => {
|
|
17
17
|
const nonceContext = (0, react_1.useMemo)(() => {
|
|
18
18
|
let counter = 0;
|
|
19
19
|
return {
|
|
@@ -23,6 +23,6 @@ const RemotionRootContexts = ({ children, numberOfAudioTags, logLevel, audioLate
|
|
|
23
23
|
const logging = (0, react_1.useMemo)(() => {
|
|
24
24
|
return { logLevel, mountTime: Date.now() };
|
|
25
25
|
}, [logLevel]);
|
|
26
|
-
return (jsx_runtime_1.jsx(log_level_context_js_1.LogLevelContext.Provider, { value: logging, children: jsx_runtime_1.jsx(nonce_js_1.NonceContext.Provider, { value: nonceContext, children: jsx_runtime_1.jsx(TimelineContext_js_1.TimelineContextProvider, { frameState: frameState, children: jsx_runtime_1.jsx(use_media_enabled_js_1.MediaEnabledProvider, { videoEnabled: videoEnabled, audioEnabled: audioEnabled, children: jsx_runtime_1.jsx(EditorProps_js_1.EditorPropsProvider, { children: jsx_runtime_1.jsx(prefetch_state_js_1.PrefetchProvider, { children: jsx_runtime_1.jsx(SequenceManager_js_1.SequenceManagerProvider, { children: jsx_runtime_1.jsx(duration_state_js_1.DurationsContextProvider, { children: jsx_runtime_1.jsx(buffering_js_1.BufferingProvider, { children: jsx_runtime_1.jsx(shared_audio_tags_js_1.SharedAudioContextProvider, { audioLatencyHint: audioLatencyHint, audioEnabled: audioEnabled, previewSampleRate: previewSampleRate, children: jsx_runtime_1.jsx(shared_audio_tags_js_1.SharedAudioTagsContextProvider, { numberOfAudioTags: numberOfAudioTags, children: children }) }) }) }) }) }) }) }) }) }) }));
|
|
26
|
+
return (jsx_runtime_1.jsx(log_level_context_js_1.LogLevelContext.Provider, { value: logging, children: jsx_runtime_1.jsx(nonce_js_1.NonceContext.Provider, { value: nonceContext, children: jsx_runtime_1.jsx(TimelineContext_js_1.TimelineContextProvider, { frameState: frameState, children: jsx_runtime_1.jsx(use_media_enabled_js_1.MediaEnabledProvider, { videoEnabled: videoEnabled, audioEnabled: audioEnabled, children: jsx_runtime_1.jsx(EditorProps_js_1.EditorPropsProvider, { children: jsx_runtime_1.jsx(prefetch_state_js_1.PrefetchProvider, { children: jsx_runtime_1.jsx(SequenceManager_js_1.SequenceManagerProvider, { children: jsx_runtime_1.jsx(duration_state_js_1.DurationsContextProvider, { children: jsx_runtime_1.jsx(buffering_js_1.BufferingProvider, { children: jsx_runtime_1.jsx(shared_audio_tags_js_1.SharedAudioContextProvider, { audioLatencyHint: audioLatencyHint, audioEnabled: audioEnabled, previewSampleRate: previewSampleRate, _experimentalKeepAudioContextAlive: _experimentalKeepAudioContextAlive, children: jsx_runtime_1.jsx(shared_audio_tags_js_1.SharedAudioTagsContextProvider, { numberOfAudioTags: numberOfAudioTags, children: children }) }) }) }) }) }) }) }) }) }) }));
|
|
27
27
|
};
|
|
28
28
|
exports.RemotionRootContexts = RemotionRootContexts;
|
|
@@ -61,6 +61,7 @@ type SharedAudioContextValue = {
|
|
|
61
61
|
suspend: () => Promise<void>;
|
|
62
62
|
getIsResumingAudioContext: () => Promise<AudioContextResumeResult> | null;
|
|
63
63
|
unscheduleAudioNode: (node: AudioBufferSourceNode) => void;
|
|
64
|
+
_experimentalKeepAudioContextAlive: boolean;
|
|
64
65
|
};
|
|
65
66
|
type SharedAudioTagsContextValue = {
|
|
66
67
|
registerAudio: (options: {
|
|
@@ -87,6 +88,7 @@ export declare const SharedAudioContextProvider: React.FC<{
|
|
|
87
88
|
readonly audioLatencyHint: AudioContextLatencyCategory;
|
|
88
89
|
readonly audioEnabled: boolean;
|
|
89
90
|
readonly previewSampleRate: number | null;
|
|
91
|
+
readonly _experimentalKeepAudioContextAlive: boolean;
|
|
90
92
|
}>;
|
|
91
93
|
export declare const SharedAudioTagsContextProvider: React.FC<{
|
|
92
94
|
readonly numberOfAudioTags: number;
|
|
@@ -88,7 +88,7 @@ const shouldSaveForLater = (state) => {
|
|
|
88
88
|
}
|
|
89
89
|
throw new Error(`Unexpected audio context state: ${state}`);
|
|
90
90
|
};
|
|
91
|
-
const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled, previewSampleRate }) => {
|
|
91
|
+
const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled, previewSampleRate, _experimentalKeepAudioContextAlive, }) => {
|
|
92
92
|
const logLevel = (0, log_level_context_js_1.useLogLevel)();
|
|
93
93
|
const sampleRate = previewSampleRate !== null && previewSampleRate !== void 0 ? previewSampleRate : 48000;
|
|
94
94
|
(0, react_1.useEffect)(() => {
|
|
@@ -104,6 +104,11 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
|
|
|
104
104
|
sampleRate,
|
|
105
105
|
});
|
|
106
106
|
const audioContextIsPlayingEventually = (0, react_1.useRef)(false);
|
|
107
|
+
const initialExperimentalKeepAudioContextAlive = (0, react_1.useRef)(_experimentalKeepAudioContextAlive);
|
|
108
|
+
if (initialExperimentalKeepAudioContextAlive.current !==
|
|
109
|
+
_experimentalKeepAudioContextAlive) {
|
|
110
|
+
throw new Error('`_experimentalKeepAudioContextAlive` cannot be changed dynamically.');
|
|
111
|
+
}
|
|
107
112
|
const isResuming = (0, react_1.useRef)(null);
|
|
108
113
|
const nextResumeAttemptId = (0, react_1.useRef)(0);
|
|
109
114
|
const audioSyncAnchor = (0, react_1.useMemo)(() => ({ value: 0 }), []);
|
|
@@ -141,7 +146,14 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
|
|
|
141
146
|
reason: 'audio context is closed',
|
|
142
147
|
};
|
|
143
148
|
}
|
|
144
|
-
|
|
149
|
+
// With _experimentalKeepAudioContextAlive, the native context stays
|
|
150
|
+
// `running` while silenced, so the state alone does not reveal that playback
|
|
151
|
+
// is paused. Queue the node like the suspend path does, otherwise it
|
|
152
|
+
// would start right away at a stale position and become audible when
|
|
153
|
+
// the gain ramps back up.
|
|
154
|
+
const saveForLater = shouldSaveForLater(currentState) ||
|
|
155
|
+
(_experimentalKeepAudioContextAlive &&
|
|
156
|
+
!audioContextIsPlayingEventually.current);
|
|
145
157
|
if (duration > 0) {
|
|
146
158
|
if (saveForLater) {
|
|
147
159
|
nodesToResume.current.set(node, {
|
|
@@ -187,7 +199,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
|
|
|
187
199
|
reason: 'missed ' + Math.abs(offset).toFixed(2) + 's',
|
|
188
200
|
};
|
|
189
201
|
};
|
|
190
|
-
}, [ctxAndGain, logLevel]);
|
|
202
|
+
}, [ctxAndGain, _experimentalKeepAudioContextAlive, logLevel]);
|
|
191
203
|
const resume = (0, react_1.useCallback)(() => {
|
|
192
204
|
if (!ctxAndGain) {
|
|
193
205
|
return Promise.resolve();
|
|
@@ -203,6 +215,13 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
|
|
|
203
215
|
node.start(r.scheduledTime, r.offset, r.duration);
|
|
204
216
|
});
|
|
205
217
|
nodesToResume.current.clear();
|
|
218
|
+
if (_experimentalKeepAudioContextAlive &&
|
|
219
|
+
ctxAndGain.audioContext.state === 'running') {
|
|
220
|
+
// The context was never suspended, so there is nothing to wait for:
|
|
221
|
+
// the resume-wait machinery, its timeout and the mute-on-failure
|
|
222
|
+
// path are not entered.
|
|
223
|
+
return Promise.resolve();
|
|
224
|
+
}
|
|
206
225
|
const resumePromise = ctxAndGain.resume();
|
|
207
226
|
const abortController = new AbortController();
|
|
208
227
|
const resumeAttemptId = nextResumeAttemptId.current++;
|
|
@@ -228,7 +247,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
|
|
|
228
247
|
// Already logged above; swallow to avoid unhandled rejection
|
|
229
248
|
// since callers (e.g. use-playback.ts) do not await this.
|
|
230
249
|
});
|
|
231
|
-
}, [ctxAndGain, logLevel]);
|
|
250
|
+
}, [ctxAndGain, _experimentalKeepAudioContextAlive, logLevel]);
|
|
232
251
|
const getIsResumingAudioContext = (0, react_1.useCallback)(() => {
|
|
233
252
|
var _a;
|
|
234
253
|
var _b;
|
|
@@ -244,8 +263,56 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
|
|
|
244
263
|
return Promise.resolve();
|
|
245
264
|
}
|
|
246
265
|
audioContextIsPlayingEventually.current = false;
|
|
266
|
+
if (_experimentalKeepAudioContextAlive) {
|
|
267
|
+
// Silence through the gain instead of suspending, so the context
|
|
268
|
+
// clock keeps running and the next resume() is instant. Audio that
|
|
269
|
+
// is already scheduled plays out silently; resume() ramps the gain
|
|
270
|
+
// back up.
|
|
271
|
+
ctxAndGain.gainNode.gain.cancelScheduledValues(ctxAndGain.audioContext.currentTime);
|
|
272
|
+
ctxAndGain.gainNode.gain.setValueAtTime(0, ctxAndGain.audioContext.currentTime);
|
|
273
|
+
return Promise.resolve();
|
|
274
|
+
}
|
|
247
275
|
return ctxAndGain.suspend();
|
|
248
|
-
}, [ctxAndGain]);
|
|
276
|
+
}, [ctxAndGain, _experimentalKeepAudioContextAlive]);
|
|
277
|
+
// With _experimentalKeepAudioContextAlive, start the context as early as
|
|
278
|
+
// possible so the first play never waits on the suspended→running transition. Where
|
|
279
|
+
// no autoplay restriction applies (e.g. Electron with
|
|
280
|
+
// `no-user-gesture-required`), the mount attempt succeeds immediately;
|
|
281
|
+
// in a browser the first user gesture starts it. suspend() never reaches
|
|
282
|
+
// the native context in this mode, so once running it stays running.
|
|
283
|
+
// The cleanup suspends the native context, since Player contexts are
|
|
284
|
+
// not close()d and must not be left running after unmount.
|
|
285
|
+
(0, react_1.useEffect)(() => {
|
|
286
|
+
if (!_experimentalKeepAudioContextAlive) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (!ctxAndGain) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (typeof window === 'undefined') {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const wake = () => {
|
|
296
|
+
if (ctxAndGain.audioContext.state === 'running') {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
ctxAndGain.resume().catch(() => {
|
|
300
|
+
// A rejection here means autoplay is still blocked; a later
|
|
301
|
+
// gesture will retry.
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
wake();
|
|
305
|
+
window.addEventListener('pointerdown', wake, {
|
|
306
|
+
capture: true,
|
|
307
|
+
passive: true,
|
|
308
|
+
});
|
|
309
|
+
window.addEventListener('keydown', wake, { capture: true, passive: true });
|
|
310
|
+
return () => {
|
|
311
|
+
window.removeEventListener('pointerdown', wake, { capture: true });
|
|
312
|
+
window.removeEventListener('keydown', wake, { capture: true });
|
|
313
|
+
ctxAndGain.suspend().catch(() => { });
|
|
314
|
+
};
|
|
315
|
+
}, [ctxAndGain, _experimentalKeepAudioContextAlive]);
|
|
249
316
|
const audioContextValue = (0, react_1.useMemo)(() => {
|
|
250
317
|
var _a, _b;
|
|
251
318
|
return {
|
|
@@ -262,6 +329,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
|
|
|
262
329
|
suspend,
|
|
263
330
|
getIsResumingAudioContext,
|
|
264
331
|
unscheduleAudioNode,
|
|
332
|
+
_experimentalKeepAudioContextAlive,
|
|
265
333
|
};
|
|
266
334
|
}, [
|
|
267
335
|
ctxAndGain,
|
|
@@ -272,6 +340,7 @@ const SharedAudioContextProvider = ({ children, audioLatencyHint, audioEnabled,
|
|
|
272
340
|
suspend,
|
|
273
341
|
getIsResumingAudioContext,
|
|
274
342
|
unscheduleAudioNode,
|
|
343
|
+
_experimentalKeepAudioContextAlive,
|
|
275
344
|
]);
|
|
276
345
|
return (jsx_runtime_1.jsx(exports.SharedAudioContext.Provider, { value: audioContextValue, children: children }));
|
|
277
346
|
};
|
|
@@ -28,19 +28,23 @@ const waitUntilActuallyResumed = (audioContext, logLevel, signal) => {
|
|
|
28
28
|
resolve(result);
|
|
29
29
|
};
|
|
30
30
|
onAbort = () => finish('cancelled');
|
|
31
|
+
const hasAudiblyStarted = (startPerformanceTime) => {
|
|
32
|
+
const outputTimestamp = audioContext.getOutputTimestamp();
|
|
33
|
+
return (startPerformanceTime !== undefined &&
|
|
34
|
+
outputTimestamp.performanceTime !== undefined &&
|
|
35
|
+
outputTimestamp.performanceTime > startPerformanceTime &&
|
|
36
|
+
outputTimestamp.contextTime !== undefined &&
|
|
37
|
+
outputTimestamp.contextTime > startCurrentTime);
|
|
38
|
+
};
|
|
31
39
|
const check = () => {
|
|
32
|
-
var _a;
|
|
33
|
-
var
|
|
40
|
+
var _a, _b;
|
|
41
|
+
var _c;
|
|
34
42
|
animationFrame = null;
|
|
35
43
|
const { currentTime } = audioContext;
|
|
36
44
|
const outputTimestamp = audioContext.getOutputTimestamp();
|
|
37
45
|
const elapsedWallClock = performance.now() - startWallClock;
|
|
38
|
-
if (startOutputPerformanceTime
|
|
39
|
-
outputTimestamp.performanceTime !==
|
|
40
|
-
outputTimestamp.performanceTime > startOutputPerformanceTime &&
|
|
41
|
-
outputTimestamp.contextTime !== undefined &&
|
|
42
|
-
outputTimestamp.contextTime > startCurrentTime) {
|
|
43
|
-
log_js_1.Log.verbose({ logLevel, tag: 'audio' }, `waitUntilActuallyResumed: getOutputTimestamp.performanceTime advanced from ${startOutputPerformanceTime.toFixed(6)} to ${outputTimestamp.performanceTime.toFixed(6)} after ${elapsedWallClock.toFixed(1)}ms. currentTime=${currentTime.toFixed(6)} (advanced by ${(currentTime - startCurrentTime).toFixed(6)}), getOutputTimestamp.performanceTime=${(_b = (_a = outputTimestamp.performanceTime) === null || _a === void 0 ? void 0 : _a.toFixed(1)) !== null && _b !== void 0 ? _b : 'undefined'}`);
|
|
46
|
+
if (hasAudiblyStarted(startOutputPerformanceTime)) {
|
|
47
|
+
log_js_1.Log.verbose({ logLevel, tag: 'audio' }, `waitUntilActuallyResumed: getOutputTimestamp.performanceTime advanced from ${startOutputPerformanceTime.toFixed(6)} to ${(_a = outputTimestamp.performanceTime) === null || _a === void 0 ? void 0 : _a.toFixed(6)} after ${elapsedWallClock.toFixed(1)}ms. currentTime=${currentTime.toFixed(6)} (advanced by ${(currentTime - startCurrentTime).toFixed(6)}), getOutputTimestamp.performanceTime=${(_c = (_b = outputTimestamp.performanceTime) === null || _b === void 0 ? void 0 : _b.toFixed(1)) !== null && _c !== void 0 ? _c : 'undefined'}`);
|
|
44
48
|
finish('resumed');
|
|
45
49
|
return;
|
|
46
50
|
}
|
|
@@ -52,6 +56,15 @@ const waitUntilActuallyResumed = (audioContext, logLevel, signal) => {
|
|
|
52
56
|
}
|
|
53
57
|
signal.addEventListener('abort', onAbort, { once: true });
|
|
54
58
|
timeout = setTimeout(() => {
|
|
59
|
+
// The success check is scheduled with requestAnimationFrame, which can
|
|
60
|
+
// be starved while the main thread is busy. Re-check once before
|
|
61
|
+
// declaring failure so a resume that actually completed is not
|
|
62
|
+
// reported as failed - the Player would otherwise mute itself even
|
|
63
|
+
// though playback was started by a user gesture and audio is running.
|
|
64
|
+
if (hasAudiblyStarted(startOutputPerformanceTime)) {
|
|
65
|
+
finish('resumed');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
55
68
|
log_js_1.Log.warn({ logLevel, tag: 'audio' }, 'WARNING: You enabled autoPlay on an unmuted <Player /> and the browser did not allow the video to be started. Remotion muted the <Player /> so it can play. To properly handle this, either set the `muted` prop or remove the `autoPlay` prop');
|
|
56
69
|
finish('failed');
|
|
57
70
|
}, RESUME_WAIT_TIMEOUT);
|
package/dist/cjs/internals.d.ts
CHANGED
|
@@ -738,6 +738,7 @@ export declare const Internals: {
|
|
|
738
738
|
readonly videoEnabled: boolean;
|
|
739
739
|
readonly audioEnabled: boolean;
|
|
740
740
|
readonly frameState: Record<string, number> | null;
|
|
741
|
+
readonly _experimentalKeepAudioContextAlive: boolean;
|
|
741
742
|
}>;
|
|
742
743
|
readonly CompositionManagerProvider: ({ children, onlyRenderComposition, currentCompositionMetadata, initialCompositions, initialCanvasContent, }: {
|
|
743
744
|
readonly children: import("react").ReactNode;
|
|
@@ -816,12 +817,14 @@ export declare const Internals: {
|
|
|
816
817
|
suspend: () => Promise<void>;
|
|
817
818
|
getIsResumingAudioContext: () => Promise<import("./audio/wait-until-actually-resumed.js").AudioContextResumeResult> | null;
|
|
818
819
|
unscheduleAudioNode: (node: AudioBufferSourceNode) => void;
|
|
820
|
+
_experimentalKeepAudioContextAlive: boolean;
|
|
819
821
|
} | null>;
|
|
820
822
|
readonly SharedAudioContextProvider: import("react").FC<{
|
|
821
823
|
readonly children: import("react").ReactNode;
|
|
822
824
|
readonly audioLatencyHint: AudioContextLatencyCategory;
|
|
823
825
|
readonly audioEnabled: boolean;
|
|
824
826
|
readonly previewSampleRate: number | null;
|
|
827
|
+
readonly _experimentalKeepAudioContextAlive: boolean;
|
|
825
828
|
}>;
|
|
826
829
|
readonly SharedAudioTagsContext: import("react").Context<{
|
|
827
830
|
registerAudio: (options: {
|
|
@@ -1214,10 +1217,11 @@ export declare const Internals: {
|
|
|
1214
1217
|
status: CanUpdateSequencePropStatusKeyframed;
|
|
1215
1218
|
}) => string | number | readonly number[] | null;
|
|
1216
1219
|
readonly makeStaticDragOverride: (value: unknown) => DragOverrideValue;
|
|
1217
|
-
readonly makeKeyframedDragOverride: ({ status, frame, value, }: {
|
|
1220
|
+
readonly makeKeyframedDragOverride: ({ status, frame, value, defaultEasing, }: {
|
|
1218
1221
|
status: CanUpdateSequencePropStatusKeyframed;
|
|
1219
1222
|
frame: number;
|
|
1220
1223
|
value: unknown;
|
|
1224
|
+
defaultEasing?: CanUpdateSequencePropStatusEasing | undefined;
|
|
1221
1225
|
}) => DragOverrideValue;
|
|
1222
1226
|
readonly resolveDragOverrideValue: ({ dragOverrideValue, frame, }: {
|
|
1223
1227
|
dragOverrideValue: DragOverrideValue | undefined;
|
package/dist/cjs/interpolate.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.assertValidInterpolateEasingOption = assertValidInterpolateEasingOption;
|
|
4
4
|
exports.assertValidInterpolatePosterizeOption = assertValidInterpolatePosterizeOption;
|
|
5
5
|
exports.interpolate = interpolate;
|
|
6
|
+
const easing_js_1 = require("./easing.js");
|
|
6
7
|
const normalize_number_js_1 = require("./normalize-number.js");
|
|
7
8
|
const angleUnits = new Set(['deg', 'rad', 'grad', 'turn']);
|
|
8
9
|
const lengthUnits = new Set([
|
|
@@ -71,11 +72,13 @@ const transformOriginCenter = { value: 50, unit: '%' };
|
|
|
71
72
|
const stringifyNumber = (value) => {
|
|
72
73
|
return String((0, normalize_number_js_1.normalizeNumber)(value));
|
|
73
74
|
};
|
|
75
|
+
class UnsupportedStringInterpolationValueError extends TypeError {
|
|
76
|
+
}
|
|
74
77
|
const parseStringInterpolationComponent = (component, value) => {
|
|
75
78
|
var _a;
|
|
76
79
|
const match = cssNumberRegex.exec(component);
|
|
77
80
|
if (match === null) {
|
|
78
|
-
throw new
|
|
81
|
+
throw new UnsupportedStringInterpolationValueError(`Cannot interpolate "${value}" because "${component}" is not a supported scale, translate, or rotate value`);
|
|
79
82
|
}
|
|
80
83
|
const unit = (_a = match[2]) !== null && _a !== void 0 ? _a : null;
|
|
81
84
|
const numberValue = Number(match[1]);
|
|
@@ -563,6 +566,57 @@ const interpolateString = ({ input, inputRange, outputRange, options, }) => {
|
|
|
563
566
|
axisRotation: hasAxisRotation,
|
|
564
567
|
});
|
|
565
568
|
};
|
|
569
|
+
const interpolateDiscreteString = ({ input, inputRange, outputRange, options, }) => {
|
|
570
|
+
if (inputRange.length === 1) {
|
|
571
|
+
return outputRange[0];
|
|
572
|
+
}
|
|
573
|
+
for (let segmentIndex = 0; segmentIndex < inputRange.length - 1; segmentIndex++) {
|
|
574
|
+
if (resolveEasingForSegment({
|
|
575
|
+
easing: options === null || options === void 0 ? void 0 : options.easing,
|
|
576
|
+
segmentIndex,
|
|
577
|
+
}) !== easing_js_1.Easing.step1) {
|
|
578
|
+
throw new TypeError('Non-numeric strings can only be interpolated using Easing.step1');
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
const posterizedInput = (options === null || options === void 0 ? void 0 : options.posterize) === undefined
|
|
582
|
+
? input
|
|
583
|
+
: Math.floor(input / options.posterize) * options.posterize;
|
|
584
|
+
const inputMin = inputRange[0];
|
|
585
|
+
const inputMax = inputRange[inputRange.length - 1];
|
|
586
|
+
let resolvedInput = posterizedInput;
|
|
587
|
+
if (resolvedInput < inputMin) {
|
|
588
|
+
if ((options === null || options === void 0 ? void 0 : options.extrapolateLeft) === 'identity') {
|
|
589
|
+
throw new TypeError('extrapolateLeft: "identity" is not supported for non-numeric strings');
|
|
590
|
+
}
|
|
591
|
+
if ((options === null || options === void 0 ? void 0 : options.extrapolateLeft) === 'wrap') {
|
|
592
|
+
const wrapRange = inputMax - inputMin;
|
|
593
|
+
resolvedInput =
|
|
594
|
+
((((resolvedInput - inputMin) % wrapRange) + wrapRange) % wrapRange) +
|
|
595
|
+
inputMin;
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
return outputRange[0];
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (resolvedInput > inputMax) {
|
|
602
|
+
if ((options === null || options === void 0 ? void 0 : options.extrapolateRight) === 'identity') {
|
|
603
|
+
throw new TypeError('extrapolateRight: "identity" is not supported for non-numeric strings');
|
|
604
|
+
}
|
|
605
|
+
if ((options === null || options === void 0 ? void 0 : options.extrapolateRight) === 'wrap') {
|
|
606
|
+
const wrapRange = inputMax - inputMin;
|
|
607
|
+
resolvedInput =
|
|
608
|
+
((((resolvedInput - inputMin) % wrapRange) + wrapRange) % wrapRange) +
|
|
609
|
+
inputMin;
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
return outputRange[outputRange.length - 1];
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
const range = findRange(resolvedInput, inputRange);
|
|
616
|
+
return resolvedInput >= inputRange[range + 1]
|
|
617
|
+
? outputRange[range + 1]
|
|
618
|
+
: outputRange[range];
|
|
619
|
+
};
|
|
566
620
|
const validateTupleOutputRange = (outputRange) => {
|
|
567
621
|
var _a;
|
|
568
622
|
const dimensions = (_a = outputRange[0]) === null || _a === void 0 ? void 0 : _a.length;
|
|
@@ -681,7 +735,32 @@ function interpolate(input, inputRange, outputRange, options) {
|
|
|
681
735
|
if (!outputRange.every((output) => typeof output === 'string' || typeof output === 'number')) {
|
|
682
736
|
throw new TypeError('outputRange must contain only numbers, or supported scale, translate, and rotate strings');
|
|
683
737
|
}
|
|
684
|
-
|
|
738
|
+
try {
|
|
739
|
+
return interpolateString({ input, inputRange, outputRange, options });
|
|
740
|
+
}
|
|
741
|
+
catch (error) {
|
|
742
|
+
if (!outputRange.every((output) => typeof output === 'string')) {
|
|
743
|
+
throw error;
|
|
744
|
+
}
|
|
745
|
+
const hasNonNumericString = outputRange.some((output) => {
|
|
746
|
+
try {
|
|
747
|
+
parseStringInterpolationValue(output);
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
catch (parseError) {
|
|
751
|
+
return parseError instanceof UnsupportedStringInterpolationValueError;
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
if (!hasNonNumericString) {
|
|
755
|
+
throw error;
|
|
756
|
+
}
|
|
757
|
+
return interpolateDiscreteString({
|
|
758
|
+
input,
|
|
759
|
+
inputRange,
|
|
760
|
+
outputRange,
|
|
761
|
+
options,
|
|
762
|
+
});
|
|
763
|
+
}
|
|
685
764
|
}
|
|
686
765
|
if (outputRange.every((output) => Array.isArray(output))) {
|
|
687
766
|
return interpolateTuple({ input, inputRange, outputRange, options });
|
package/dist/cjs/use-schema.d.ts
CHANGED
|
@@ -90,10 +90,11 @@ export type GetEffectPropStatuses = (nodePath: SequencePropsSubscriptionKey, eff
|
|
|
90
90
|
export type GetDragOverrides = (nodePath: SequencePropsSubscriptionKey) => DragOverrides[string];
|
|
91
91
|
export type GetEffectDragOverrides = (nodePath: SequencePropsSubscriptionKey, effectIndex: number) => Record<string, DragOverrideValue>;
|
|
92
92
|
export declare const makeStaticDragOverride: (value: unknown) => DragOverrideValue;
|
|
93
|
-
export declare const makeKeyframedDragOverride: ({ status, frame, value, }: {
|
|
93
|
+
export declare const makeKeyframedDragOverride: ({ status, frame, value, defaultEasing, }: {
|
|
94
94
|
status: CanUpdateSequencePropStatusKeyframed;
|
|
95
95
|
frame: number;
|
|
96
96
|
value: unknown;
|
|
97
|
+
defaultEasing?: CanUpdateSequencePropStatusEasing | undefined;
|
|
97
98
|
}) => DragOverrideValue;
|
|
98
99
|
export declare const getStaticDragOverrideValue: (dragOverrideValue: DragOverrideValue | undefined) => unknown;
|
|
99
100
|
export declare const isKeyframedStatus: (status: CanUpdateSequencePropStatus | null) => status is CanUpdateSequencePropStatusKeyframed;
|
package/dist/cjs/use-schema.js
CHANGED
|
@@ -19,7 +19,7 @@ const makeStaticDragOverride = (value) => {
|
|
|
19
19
|
return { type: 'static', value };
|
|
20
20
|
};
|
|
21
21
|
exports.makeStaticDragOverride = makeStaticDragOverride;
|
|
22
|
-
const makeKeyframedDragOverride = ({ status, frame, value, }) => {
|
|
22
|
+
const makeKeyframedDragOverride = ({ status, frame, value, defaultEasing = exports.DEFAULT_LINEAR_EASING, }) => {
|
|
23
23
|
const existingIndex = status.keyframes.findIndex((keyframe) => keyframe.frame === frame);
|
|
24
24
|
const keyframes = existingIndex === -1
|
|
25
25
|
? [...status.keyframes, { frame, value }].sort((first, second) => first.frame - second.frame)
|
|
@@ -33,12 +33,12 @@ const makeKeyframedDragOverride = ({ status, frame, value, }) => {
|
|
|
33
33
|
keyframeCount: keyframes.length,
|
|
34
34
|
});
|
|
35
35
|
const easingToDuplicate = easingIndexToDuplicate === null
|
|
36
|
-
?
|
|
36
|
+
? defaultEasing
|
|
37
37
|
: easing[easingIndexToDuplicate];
|
|
38
38
|
easing.splice(insertedKeyframeIndex, 0, easingToDuplicate);
|
|
39
39
|
}
|
|
40
40
|
while (easing.length < keyframes.length - 1) {
|
|
41
|
-
easing.push(
|
|
41
|
+
easing.push(defaultEasing);
|
|
42
42
|
}
|
|
43
43
|
if (easing.length > keyframes.length - 1) {
|
|
44
44
|
easing.length = keyframes.length - 1;
|
package/dist/cjs/version.d.ts
CHANGED
package/dist/cjs/version.js
CHANGED