@open-motion/core 0.0.1-alpha.0

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.
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { VideoConfig } from './index';
3
+ export interface PlayerProps {
4
+ component: React.ComponentType<any>;
5
+ config: VideoConfig;
6
+ inputProps?: any;
7
+ controls?: boolean;
8
+ autoPlay?: boolean;
9
+ loop?: boolean;
10
+ }
11
+ export declare const Player: React.FC<PlayerProps>;
package/dist/Player.js ADDED
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Player = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const index_1 = require("./index");
7
+ const Player = ({ component: Component, config, inputProps = {}, controls = true, autoPlay = false, loop = false, }) => {
8
+ const [frame, setFrame] = (0, react_1.useState)(0);
9
+ const [isPlaying, setIsPlaying] = (0, react_1.useState)(autoPlay);
10
+ const requestRef = (0, react_1.useRef)();
11
+ const lastTimeRef = (0, react_1.useRef)();
12
+ const animate = (0, react_1.useCallback)((time) => {
13
+ if (lastTimeRef.current !== undefined) {
14
+ const deltaTime = time - lastTimeRef.current;
15
+ const frameStep = (deltaTime / 1000) * config.fps;
16
+ setFrame((prevFrame) => {
17
+ let nextFrame = prevFrame + frameStep;
18
+ if (nextFrame >= config.durationInFrames) {
19
+ if (loop) {
20
+ nextFrame = 0;
21
+ }
22
+ else {
23
+ nextFrame = config.durationInFrames - 1;
24
+ setIsPlaying(false);
25
+ }
26
+ }
27
+ return nextFrame;
28
+ });
29
+ }
30
+ lastTimeRef.current = time;
31
+ requestRef.current = requestAnimationFrame(animate);
32
+ }, [config.fps, config.durationInFrames, loop]);
33
+ (0, react_1.useEffect)(() => {
34
+ if (isPlaying) {
35
+ lastTimeRef.current = undefined;
36
+ requestRef.current = requestAnimationFrame(animate);
37
+ }
38
+ else {
39
+ if (requestRef.current) {
40
+ cancelAnimationFrame(requestRef.current);
41
+ }
42
+ }
43
+ return () => {
44
+ if (requestRef.current) {
45
+ cancelAnimationFrame(requestRef.current);
46
+ }
47
+ };
48
+ }, [isPlaying, animate]);
49
+ const togglePlay = () => setIsPlaying(!isPlaying);
50
+ const handleSeek = (e) => {
51
+ setFrame(Number(e.target.value));
52
+ setIsPlaying(false);
53
+ };
54
+ return ((0, jsx_runtime_1.jsxs)("div", { style: { display: 'inline-block', border: '1px solid #ccc', borderRadius: '4px', overflow: 'hidden' }, children: [(0, jsx_runtime_1.jsx)("div", { style: {
55
+ width: config.width,
56
+ height: config.height,
57
+ position: 'relative',
58
+ background: '#000',
59
+ overflow: 'hidden'
60
+ }, children: (0, jsx_runtime_1.jsx)(index_1.CompositionProvider, { config: config, frame: Math.floor(frame), inputProps: inputProps, children: (0, jsx_runtime_1.jsx)(Component, {}) }) }), controls && ((0, jsx_runtime_1.jsxs)("div", { style: { padding: '10px', background: '#f0f0f0', display: 'flex', alignItems: 'center', gap: '10px' }, children: [(0, jsx_runtime_1.jsx)("button", { onClick: togglePlay, children: isPlaying ? 'Pause' : 'Play' }), (0, jsx_runtime_1.jsx)("input", { type: "range", min: "0", max: config.durationInFrames - 1, step: "1", value: Math.floor(frame), onChange: handleSeek, style: { flex: 1 } }), (0, jsx_runtime_1.jsxs)("div", { style: { minWidth: '80px', fontSize: '12px', textAlign: 'right' }, children: [Math.floor(frame), " / ", config.durationInFrames] })] }))] }));
61
+ };
62
+ exports.Player = Player;
@@ -0,0 +1,77 @@
1
+ import React from 'react';
2
+ export interface VideoConfig {
3
+ width: number;
4
+ height: number;
5
+ fps: number;
6
+ durationInFrames: number;
7
+ }
8
+ export declare const CompositionProvider: React.FC<{
9
+ config: VideoConfig;
10
+ frame: number;
11
+ inputProps?: any;
12
+ children: React.ReactNode;
13
+ }>;
14
+ export declare const useVideoConfig: () => VideoConfig;
15
+ export declare const useCurrentFrame: () => number;
16
+ export declare const useAbsoluteFrame: () => number;
17
+ export declare const getInputProps: <T extends any>() => T;
18
+ export interface CompositionProps extends VideoConfig {
19
+ id: string;
20
+ component: React.ComponentType<any>;
21
+ }
22
+ export declare const registerComposition: (props: CompositionProps) => void;
23
+ export declare const getCompositions: () => CompositionProps[];
24
+ export declare const getCompositionById: (id: string) => CompositionProps | undefined;
25
+ /**
26
+ * Composition Component (Remotion compatible)
27
+ */
28
+ export declare const Composition: React.FC<CompositionProps>;
29
+ /**
30
+ * delayRender: Signal that an async resource is being loaded.
31
+ */
32
+ export declare const delayRender: (label?: string) => number;
33
+ /**
34
+ * continueRender: Signal that an async resource has finished loading.
35
+ */
36
+ export declare const continueRender: (handle: number) => void;
37
+ /**
38
+ * interpolate function: maps a value from one range to another.
39
+ * Compatible with Remotion's interpolate.
40
+ */
41
+ export declare const interpolate: (input: number, inputRange: [number, number], outputRange: [number, number], options?: {
42
+ extrapolateLeft?: "extrapolate" | "clamp";
43
+ extrapolateRight?: "extrapolate" | "clamp";
44
+ }) => number;
45
+ /**
46
+ * Time Hijacking Bridge Script
47
+ */
48
+ export declare const getTimeHijackScript: (frame: number, fps: number) => string;
49
+ /**
50
+ * Sequence Component
51
+ */
52
+ export declare const Sequence: React.FC<{
53
+ from: number;
54
+ durationInFrames?: number;
55
+ children: React.ReactNode;
56
+ }>;
57
+ /**
58
+ * Spring animation logic
59
+ */
60
+ export declare const spring: ({ frame, fps, config, }: {
61
+ frame: number;
62
+ fps: number;
63
+ config?: {
64
+ stiffness?: number;
65
+ damping?: number;
66
+ mass?: number;
67
+ };
68
+ }) => number;
69
+ /**
70
+ * Audio Component
71
+ */
72
+ export declare const Audio: React.FC<{
73
+ src: string;
74
+ startFrom?: number;
75
+ volume?: number;
76
+ }>;
77
+ export * from './Player';
package/dist/index.js ADDED
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.Audio = exports.spring = exports.Sequence = exports.getTimeHijackScript = exports.interpolate = exports.continueRender = exports.delayRender = exports.Composition = exports.getCompositionById = exports.getCompositions = exports.registerComposition = exports.getInputProps = exports.useAbsoluteFrame = exports.useCurrentFrame = exports.useVideoConfig = exports.CompositionProvider = void 0;
18
+ const jsx_runtime_1 = require("react/jsx-runtime");
19
+ const react_1 = require("react");
20
+ const VideoConfigContext = (0, react_1.createContext)(null);
21
+ const FrameContext = (0, react_1.createContext)(0);
22
+ const AbsoluteFrameContext = (0, react_1.createContext)(0);
23
+ const InputPropsContext = (0, react_1.createContext)({});
24
+ const CompositionProvider = ({ config, frame, inputProps = {}, children }) => {
25
+ return ((0, jsx_runtime_1.jsx)(VideoConfigContext.Provider, { value: config, children: (0, jsx_runtime_1.jsx)(InputPropsContext.Provider, { value: inputProps, children: (0, jsx_runtime_1.jsx)(AbsoluteFrameContext.Provider, { value: frame, children: (0, jsx_runtime_1.jsx)(FrameContext.Provider, { value: frame, children: children }) }) }) }));
26
+ };
27
+ exports.CompositionProvider = CompositionProvider;
28
+ const useVideoConfig = () => {
29
+ const context = (0, react_1.useContext)(VideoConfigContext);
30
+ if (!context)
31
+ throw new Error('useVideoConfig must be used within CompositionProvider');
32
+ return context;
33
+ };
34
+ exports.useVideoConfig = useVideoConfig;
35
+ const useCurrentFrame = () => {
36
+ return (0, react_1.useContext)(FrameContext);
37
+ };
38
+ exports.useCurrentFrame = useCurrentFrame;
39
+ const useAbsoluteFrame = () => {
40
+ return (0, react_1.useContext)(AbsoluteFrameContext);
41
+ };
42
+ exports.useAbsoluteFrame = useAbsoluteFrame;
43
+ const getInputProps = () => {
44
+ return (0, react_1.useContext)(InputPropsContext);
45
+ };
46
+ exports.getInputProps = getInputProps;
47
+ const compositions = new Map();
48
+ const registerComposition = (props) => {
49
+ compositions.set(props.id, props);
50
+ if (typeof window !== 'undefined') {
51
+ window.__OPEN_MOTION_COMPOSITIONS__ = Array.from(compositions.values());
52
+ }
53
+ };
54
+ exports.registerComposition = registerComposition;
55
+ const getCompositions = () => Array.from(compositions.values());
56
+ exports.getCompositions = getCompositions;
57
+ const getCompositionById = (id) => compositions.get(id);
58
+ exports.getCompositionById = getCompositionById;
59
+ /**
60
+ * Composition Component (Remotion compatible)
61
+ */
62
+ const Composition = (props) => {
63
+ (0, exports.registerComposition)(props);
64
+ return null;
65
+ };
66
+ exports.Composition = Composition;
67
+ // --- Rendering Synchronization ---
68
+ let delayRenderCounter = 0;
69
+ /**
70
+ * delayRender: Signal that an async resource is being loaded.
71
+ */
72
+ const delayRender = (label) => {
73
+ const handle = delayRenderCounter++;
74
+ console.debug(`OpenMotion: delayRender[${handle}] ${label || ''}`);
75
+ if (typeof window !== 'undefined') {
76
+ window.__OPEN_MOTION_DELAY_RENDER_COUNT__ = (window.__OPEN_MOTION_DELAY_RENDER_COUNT__ || 0) + 1;
77
+ }
78
+ return handle;
79
+ };
80
+ exports.delayRender = delayRender;
81
+ /**
82
+ * continueRender: Signal that an async resource has finished loading.
83
+ */
84
+ const continueRender = (handle) => {
85
+ console.debug(`OpenMotion: continueRender[${handle}]`);
86
+ if (typeof window !== 'undefined') {
87
+ window.__OPEN_MOTION_DELAY_RENDER_COUNT__ = Math.max(0, (window.__OPEN_MOTION_DELAY_RENDER_COUNT__ || 0) - 1);
88
+ }
89
+ };
90
+ exports.continueRender = continueRender;
91
+ /**
92
+ * interpolate function: maps a value from one range to another.
93
+ * Compatible with Remotion's interpolate.
94
+ */
95
+ const interpolate = (input, inputRange, outputRange, options) => {
96
+ const [minInput, maxInput] = inputRange;
97
+ const [minOutput, maxOutput] = outputRange;
98
+ let result = minOutput + ((input - minInput) / (maxInput - minInput)) * (maxOutput - minOutput);
99
+ if (options?.extrapolateLeft === 'clamp' && input < minInput)
100
+ return minOutput;
101
+ if (options?.extrapolateRight === 'clamp' && input > maxInput)
102
+ return maxOutput;
103
+ return result;
104
+ };
105
+ exports.interpolate = interpolate;
106
+ /**
107
+ * Time Hijacking Bridge Script
108
+ */
109
+ const getTimeHijackScript = (frame, fps) => {
110
+ const timeMs = (frame / fps) * 1000;
111
+ return `
112
+ (function() {
113
+ const timeMs = ${timeMs};
114
+ const frame = ${frame};
115
+
116
+ const OriginalDate = window.Date;
117
+ function MockDate() {
118
+ return new OriginalDate(timeMs);
119
+ }
120
+ MockDate.now = () => timeMs;
121
+ MockDate.parse = OriginalDate.parse;
122
+ MockDate.UTC = OriginalDate.UTC;
123
+ MockDate.prototype = OriginalDate.prototype;
124
+ window.Date = MockDate;
125
+
126
+ if (window.performance) {
127
+ window.performance.now = () => timeMs;
128
+ }
129
+
130
+ window.requestAnimationFrame = (callback) => {
131
+ return setTimeout(() => callback(timeMs), 0);
132
+ };
133
+ window.cancelAnimationFrame = (id) => clearTimeout(id);
134
+
135
+ console.debug('OpenMotion: Time hijacked for frame ' + frame + ' at ' + timeMs + 'ms');
136
+ })();
137
+ `;
138
+ };
139
+ exports.getTimeHijackScript = getTimeHijackScript;
140
+ /**
141
+ * Sequence Component
142
+ */
143
+ const Sequence = ({ from, durationInFrames, children }) => {
144
+ const currentFrame = (0, exports.useCurrentFrame)();
145
+ const relativeFrame = currentFrame - from;
146
+ const isVisible = relativeFrame >= 0 && (durationInFrames === undefined || relativeFrame < durationInFrames);
147
+ if (!isVisible)
148
+ return null;
149
+ return ((0, jsx_runtime_1.jsx)(FrameContext.Provider, { value: relativeFrame, children: children }));
150
+ };
151
+ exports.Sequence = Sequence;
152
+ /**
153
+ * Spring animation logic
154
+ */
155
+ const spring = ({ frame, fps, config = { stiffness: 100, damping: 10, mass: 1 }, }) => {
156
+ const { stiffness = 100, damping = 10, mass = 1 } = config;
157
+ const t = frame / fps;
158
+ const zeta = damping / (2 * Math.sqrt(stiffness * mass));
159
+ const omega0 = Math.sqrt(stiffness / mass);
160
+ if (zeta < 1) {
161
+ const omegaD = omega0 * Math.sqrt(1 - zeta * zeta);
162
+ const envelope = Math.exp(-zeta * omega0 * t);
163
+ return 1 - envelope * (Math.cos(omegaD * t) + (zeta * omega0 / omegaD) * Math.sin(omegaD * t));
164
+ }
165
+ else {
166
+ return 1 - Math.exp(-omega0 * t) * (1 + omega0 * t);
167
+ }
168
+ };
169
+ exports.spring = spring;
170
+ /**
171
+ * Audio Component
172
+ */
173
+ const Audio = (props) => {
174
+ if (typeof window !== 'undefined') {
175
+ window.__OPEN_MOTION_AUDIO_ASSETS__ = window.__OPEN_MOTION_AUDIO_ASSETS__ || [];
176
+ if (!window.__OPEN_MOTION_AUDIO_ASSETS__.find((a) => a.src === props.src)) {
177
+ window.__OPEN_MOTION_AUDIO_ASSETS__.push(props);
178
+ }
179
+ }
180
+ return null;
181
+ };
182
+ exports.Audio = Audio;
183
+ __exportStar(require("./Player"), exports);
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@open-motion/core",
3
+ "version": "0.0.1-alpha.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/open-motion/open-motion.git",
10
+ "directory": "packages/core"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "dependencies": {},
19
+ "peerDependencies": {
20
+ "react": "^18.0.0"
21
+ },
22
+ "scripts": {
23
+ "build": "tsc"
24
+ }
25
+ }