From 5165d15e7d519542592d8844066ece09a9113eea Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 03:20:59 +0000 Subject: [PATCH] feat: implement production-grade Lottie animation integration - Created component with frame-perfect synchronization. - Implemented deterministic rendering by manually driving Lottie frames. - Added support for relative frame contexts in Sequence component. - Updated create-motionforge templates with Lottie examples and dependencies. - Added comprehensive documentation and demo assets. - Bumped motionforge to v1.3.0 and create-motionforge to v1.1.0. Co-authored-by: codedbytahir <200578194+codedbytahir@users.noreply.github.com> --- dev_server.log | 12 -- packages/create-motionforge/package.json | 2 +- .../templates/hello-world/page.tsx.template | 14 +- .../templates/shared/package.json.template | 5 +- packages/motionforge/CHANGELOG.md | 14 ++ packages/motionforge/README.md | 44 +++++- packages/motionforge/bun.lock | 6 +- packages/motionforge/package.json | 5 +- .../motionforge/src/components/Lottie.tsx | 127 ++++++++++++++++++ packages/motionforge/src/components/Media.tsx | 7 +- .../motionforge/src/components/Sequence.tsx | 2 +- packages/motionforge/src/core/types.ts | 12 ++ packages/motionforge/src/demo/LottieDemo.tsx | 73 ++++++++++ .../motionforge/src/demo/sample-lottie.json | 35 +++++ packages/motionforge/src/index.ts | 4 +- src/lib/remotion/components/Lottie.tsx | 127 ++++++++++++++++++ src/lib/remotion/components/Media.tsx | 7 +- src/lib/remotion/components/Sequence.tsx | 2 +- src/lib/remotion/core/types.ts | 12 ++ src/lib/remotion/demo/LottieDemo.tsx | 73 ++++++++++ src/lib/remotion/demo/sample-lottie.json | 35 +++++ src/lib/remotion/index.ts | 4 +- 22 files changed, 586 insertions(+), 36 deletions(-) delete mode 100644 dev_server.log create mode 100644 packages/motionforge/src/components/Lottie.tsx create mode 100644 packages/motionforge/src/demo/LottieDemo.tsx create mode 100644 packages/motionforge/src/demo/sample-lottie.json create mode 100644 src/lib/remotion/components/Lottie.tsx create mode 100644 src/lib/remotion/demo/LottieDemo.tsx create mode 100644 src/lib/remotion/demo/sample-lottie.json diff --git a/dev_server.log b/dev_server.log deleted file mode 100644 index e3c9e49..0000000 --- a/dev_server.log +++ /dev/null @@ -1,12 +0,0 @@ -$ next dev -p 3000 2>&1 | tee dev.log -⨯ Failed to start server -Error: listen EADDRINUSE: address already in use :::3000 - at (Error: listen EADDRINUSE: address already in use :::3000) - at new Promise () { - code: 'EADDRINUSE', - errno: -98, - syscall: 'listen', - address: '::', - port: 3000 -} -[?25h diff --git a/packages/create-motionforge/package.json b/packages/create-motionforge/package.json index b24d310..499afd7 100644 --- a/packages/create-motionforge/package.json +++ b/packages/create-motionforge/package.json @@ -1,6 +1,6 @@ { "name": "create-motionforge", - "version": "1.0.0", + "version": "1.1.0", "description": "CLI tool to bootstrap MotionForge projects", "bin": { "create-motionforge": "./bin/index.js" diff --git a/packages/create-motionforge/templates/hello-world/page.tsx.template b/packages/create-motionforge/templates/hello-world/page.tsx.template index 0f5e5ec..6fd7f4e 100644 --- a/packages/create-motionforge/templates/hello-world/page.tsx.template +++ b/packages/create-motionforge/templates/hello-world/page.tsx.template @@ -1,7 +1,7 @@ 'use client'; import React from 'react'; -import { Player, AbsoluteFill, useCurrentFrame, interpolate, spring, useVideoConfig } from 'motionforge'; +import { Player, AbsoluteFill, useCurrentFrame, interpolate, spring, useVideoConfig, LottieAnimation } from 'motionforge'; const HelloWorldComposition = () => { const frame = useCurrentFrame(); @@ -20,7 +20,7 @@ const HelloWorldComposition = () => { const translateY = interpolate(frame, [0, 30], [50, 0]); return ( - +
{ >

Hello MotionForge!

Your programmatic video journey starts here.

+ + {/* Lottie Example: Clean, deterministic, and frame-synced */} +
+
+ +
+
); diff --git a/packages/create-motionforge/templates/shared/package.json.template b/packages/create-motionforge/templates/shared/package.json.template index c7661c2..016a95d 100644 --- a/packages/create-motionforge/templates/shared/package.json.template +++ b/packages/create-motionforge/templates/shared/package.json.template @@ -9,12 +9,13 @@ "lint": "next lint" }, "dependencies": { - "motionforge": "^1.2.0", + "motionforge": "^1.3.0", "next": "15.1.0", "react": "^19.0.0", "react-dom": "^19.0.0", "framer-motion": "^11.13.1", - "lucide-react": "^0.468.0" + "lucide-react": "^0.468.0", + "lottie-web": "^5.12.2" }, "devDependencies": { "@types/node": "^20", diff --git a/packages/motionforge/CHANGELOG.md b/packages/motionforge/CHANGELOG.md index 963af21..1c501ae 100644 --- a/packages/motionforge/CHANGELOG.md +++ b/packages/motionforge/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to MotionForge will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.3.0] - 2024-02-25 + +### Added +- Production-grade Lottie animation support with `` +- Frame-perfect synchronization between Lottie and MotionForge timeline +- Deterministic Lottie rendering (disabled internal requestAnimationFrame) +- Support for relative frame contexts in `Sequence` +- New Lottie demo and sample assets +- Lottie support in `create-motionforge` CLI templates + +### Changed +- Updated `useRelativeCurrentFrame` to support `null` as default value for better sequence detection +- Improved CLI template to include `lottie-web` by default + ## [1.2.0] - 2024-01-15 ### Added diff --git a/packages/motionforge/README.md b/packages/motionforge/README.md index b450538..634a62d 100644 --- a/packages/motionforge/README.md +++ b/packages/motionforge/README.md @@ -18,6 +18,7 @@ - 🎬 **Frame-Based Rendering** - Precise control over every frame - 🎨 **70+ Effect Components** - Fade, Scale, Slide, 3D transforms, particles, and more - 🌊 **Spring Physics** - Natural, physics-based animations +- 🎬 **Lottie Support** - Full integration with Lottie-web for vector animations - 📊 **Interpolation System** - Smooth transitions with 20+ easing functions - 🎮 **Interactive Player** - Real-time preview with timeline controls - 📦 **Frame Caching** - LRU cache for optimized performance @@ -42,6 +43,8 @@ bun add motionforge ## 🚀 Quick Start +### Basic Animation + ```tsx import { AbsoluteFill, @@ -87,8 +90,38 @@ const MyVideo = () => {
); }; +``` + +### Using Lottie + +MotionForge provides first-class support for Lottie animations, fully synchronized with the frame system. + +```tsx +import { LottieAnimation } from 'motionforge'; +import myAnimation from './animation.json'; + +const MyComp = () => { + return ( + + ); +}; +``` + +**Properties for ``:** +- `src`: JSON object or URL to the Lottie file. +- `frameStart`: Frame to start from (default: 0). +- `frameEnd`: Frame to end at (default: animation end). +- `playbackRate`: Speed of playback (default: 1). +- `loop`: Whether to loop the animation (default: false). +- `width`/`height`: Dimensions of the container. -// Use with Player +### Player Integration + +```tsx const App = () => ( ( | `Reverse` | Play content backwards | | `Series` | Sequential scene management | +### Media Components + +| Component | Description | +|-----------|-------------| +| `Video` | Frame-synced video playback | +| `Audio` | Frame-synced audio playback | +| `Img` | Static image component | +| `LottieAnimation` | Production-grade, frame-synced Lottie animations | + ### Effect Components ```tsx diff --git a/packages/motionforge/bun.lock b/packages/motionforge/bun.lock index 4886487..8e224f3 100644 --- a/packages/motionforge/bun.lock +++ b/packages/motionforge/bun.lock @@ -1,9 +1,11 @@ { "lockfileVersion": 1, - "configVersion": 1, "workspaces": { "": { "name": "motionforge", + "dependencies": { + "lottie-web": "^5.13.0", + }, "devDependencies": { "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", @@ -171,6 +173,8 @@ "load-tsconfig": ["load-tsconfig@0.2.5", "", {}, "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg=="], + "lottie-web": ["lottie-web@5.13.0", "", {}, "sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], diff --git a/packages/motionforge/package.json b/packages/motionforge/package.json index 09013e4..c9113a9 100644 --- a/packages/motionforge/package.json +++ b/packages/motionforge/package.json @@ -1,6 +1,6 @@ { "name": "motionforge", - "version": "1.2.0", + "version": "1.3.0", "description": "A React-based framework for creating videos programmatically. Build stunning videos with React components, spring animations, and frame-perfect control.", "author": "MotionForge Team", "license": "MIT", @@ -87,5 +87,8 @@ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" + }, + "dependencies": { + "lottie-web": "^5.13.0" } } diff --git a/packages/motionforge/src/components/Lottie.tsx b/packages/motionforge/src/components/Lottie.tsx new file mode 100644 index 0000000..3ce0ba3 --- /dev/null +++ b/packages/motionforge/src/components/Lottie.tsx @@ -0,0 +1,127 @@ +'use client'; + +import React, { useEffect, useRef, useState } from 'react'; +import lottie, { AnimationItem, AnimationConfigWithData, AnimationConfigWithPath } from 'lottie-web'; +import { useCurrentFrame, useVideoConfig } from '../core/context'; +import { useRelativeCurrentFrame } from './Sequence'; +import { LottieAnimationProps } from '../core/types'; + +/** + * LottieAnimation Component + * + * Renders a Lottie animation synchronized with the MotionForge frame system. + * This component is deterministic and does not use requestAnimationFrame for playback. + */ +export const LottieAnimation: React.FC = ({ + src, + frameStart = 0, + frameEnd, + playbackRate = 1, + loop = false, + width, + height, + style, + className, +}) => { + const containerRef = useRef(null); + const animationRef = useRef(null); + + // Use relative frame if inside a Sequence, otherwise use absolute frame + const relativeFrame = useRelativeCurrentFrame(); + const absoluteFrame = useCurrentFrame(); + const currentFrame = relativeFrame !== null ? relativeFrame : absoluteFrame; + + const [isLoaded, setIsLoaded] = useState(false); + + // Initialize Lottie instance + useEffect(() => { + if (!containerRef.current) return; + + // Clear container to prevent duplicate renders + containerRef.current.innerHTML = ''; + + const commonConfig = { + container: containerRef.current, + renderer: 'svg' as const, + loop: false, // We control playback manually + autoplay: false, // We control playback manually + }; + + let anim: AnimationItem; + + if (typeof src === 'string') { + anim = lottie.loadAnimation({ + ...commonConfig, + path: src, + } as AnimationConfigWithPath); + } else { + // Create a deep copy of animationData to prevent lottie-web from mutating it + anim = lottie.loadAnimation({ + ...commonConfig, + animationData: JSON.parse(JSON.stringify(src)), + } as AnimationConfigWithData); + } + + animationRef.current = anim; + + const handleLoaded = () => { + setIsLoaded(true); + }; + + // Lottie emits DOMLoaded when the SVG elements are added to the DOM + anim.addEventListener('DOMLoaded', handleLoaded); + + return () => { + anim.removeEventListener('DOMLoaded', handleLoaded); + anim.destroy(); + animationRef.current = null; + }; + }, [src]); + + // Synchronize Lottie frame with MotionForge frame + useEffect(() => { + if (!animationRef.current || !isLoaded) return; + + const anim = animationRef.current; + + // totalFrames is the number of frames in the Lottie animation + const totalLottieFrames = anim.totalFrames; + + // Determine the effective end frame for the animation window + const end = frameEnd !== undefined ? frameEnd : totalLottieFrames; + const duration = end - frameStart; + + // Calculate the target frame in the Lottie animation + let lottieFrame = (currentFrame * playbackRate) + frameStart; + + if (loop && duration > 0) { + // Loop within the specified window [frameStart, end] + lottieFrame = frameStart + ((lottieFrame - frameStart) % duration); + } else { + // Clamp to the end frame if not looping + lottieFrame = Math.min(lottieFrame, end); + } + + // Go to the calculated frame and stop (prevent internal playback) + // The second parameter 'true' indicates we are using frame numbers, not time. + anim.goToAndStop(lottieFrame, true); + }, [currentFrame, isLoaded, frameStart, frameEnd, playbackRate, loop]); + + return ( +
+ ); +}; + +export default LottieAnimation; diff --git a/packages/motionforge/src/components/Media.tsx b/packages/motionforge/src/components/Media.tsx index c081791..0686fc7 100755 --- a/packages/motionforge/src/components/Media.tsx +++ b/packages/motionforge/src/components/Media.tsx @@ -6,7 +6,7 @@ import { interpolate } from '../utils/animation'; // Absolute Fill - Container component interface AbsoluteFillProps { - children: React.ReactNode; + children?: React.ReactNode; style?: React.CSSProperties; className?: string; } @@ -223,11 +223,6 @@ export const Img: React.FC = ({ }; // StaticFile component - for local static assets -interface StaticFileProps { - src: string; - style?: React.CSSProperties; -} - export const staticFile = (path: string): string => { // In a real implementation, this would resolve to a static file path return `/static/${path}`; diff --git a/packages/motionforge/src/components/Sequence.tsx b/packages/motionforge/src/components/Sequence.tsx index 16eac52..6226ae9 100755 --- a/packages/motionforge/src/components/Sequence.tsx +++ b/packages/motionforge/src/components/Sequence.tsx @@ -107,7 +107,7 @@ const SequenceFrameProvider: React.FC = ({ }; // Relative Frame Context -const RelativeFrameContext = createContext(0); +const RelativeFrameContext = createContext(null); export const useRelativeCurrentFrame = () => useContext(RelativeFrameContext); diff --git a/packages/motionforge/src/core/types.ts b/packages/motionforge/src/core/types.ts index 68d3c6b..1aa2530 100755 --- a/packages/motionforge/src/core/types.ts +++ b/packages/motionforge/src/core/types.ts @@ -59,6 +59,18 @@ export interface TextProps { children: React.ReactNode; } +export interface LottieAnimationProps { + src: string | object; + frameStart?: number; + frameEnd?: number; + playbackRate?: number; + loop?: boolean; + width?: number | string; + height?: number | string; + style?: React.CSSProperties; + className?: string; +} + export interface SpringConfig { frame: number; fps: number; diff --git a/packages/motionforge/src/demo/LottieDemo.tsx b/packages/motionforge/src/demo/LottieDemo.tsx new file mode 100644 index 0000000..7b0fba3 --- /dev/null +++ b/packages/motionforge/src/demo/LottieDemo.tsx @@ -0,0 +1,73 @@ +'use client'; + +import React from 'react'; +import { AbsoluteFill, Sequence, LottieAnimation, Text } from '../index'; +import sampleLottie from './sample-lottie.json'; + +export const LottieDemo: React.FC = () => { + return ( + +
+

Lottie Integration

+

+ Production-grade Lottie animations with frame-perfect synchronization. +

+
+ +
+ {/* Basic Usage */} +
+
+ +
+ Basic Playback +
+ + {/* Custom Start/End */} +
+
+ +
+ Partial Window (Looping) +
+ + {/* Playback Rate */} +
+
+ +
+ Slow Motion (0.5x) +
+ + {/* Inside a Sequence */} +
+ +
+ +
+ Inside Sequence (Starts at frame 60) +
+
+
+ + +
+ Deterministic Rendering System +
+
+
+ ); +}; + +export default LottieDemo; diff --git a/packages/motionforge/src/demo/sample-lottie.json b/packages/motionforge/src/demo/sample-lottie.json new file mode 100644 index 0000000..428b4c8 --- /dev/null +++ b/packages/motionforge/src/demo/sample-lottie.json @@ -0,0 +1,35 @@ +{ + "v": "5.7.4", + "fr": 30, + "ip": 0, + "op": 60, + "w": 500, + "h": 500, + "nm": "Sample Square", + "ddd": 0, + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 1, + "nm": "Square", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 1, "k": [{ "i": { "x": [0.833], "y": [0.833] }, "o": { "x": [0.167], "y": [0.167] }, "t": 0, "s": [0] }, { "t": 60, "s": [360] }], "ix": 10 }, + "p": { "a": 1, "k": [{ "i": { "x": 0.833, "y": 0.833 }, "o": { "x": 0.167, "y": 0.167 }, "t": 0, "s": [250, 250, 0], "to": [0, 0, 0], "ti": [0, 0, 0] }, { "t": 60, "s": [250, 250, 0] }], "ix": 2 }, + "a": { "a": 0, "k": [25, 25, 0], "ix": 1 }, + "s": { "a": 1, "k": [{ "i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] }, "o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] }, "t": 0, "s": [100, 100, 100] }, { "i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] }, "o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] }, "t": 30, "s": [200, 200, 100] }, { "t": 60, "s": [100, 100, 100] }], "ix": 6 } + }, + "ao": 0, + "sw": 50, + "sh": 50, + "sc": "#10b981", + "ip": 0, + "op": 60, + "st": 0, + "bm": 0 + } + ] +} diff --git a/packages/motionforge/src/index.ts b/packages/motionforge/src/index.ts index c73095c..e481f69 100755 --- a/packages/motionforge/src/index.ts +++ b/packages/motionforge/src/index.ts @@ -6,7 +6,7 @@ * marketing videos, and automated video generation. * * @package MotionForge - * @version 1.2.0 + * @version 1.3.0 * @license MIT */ @@ -47,6 +47,7 @@ export { G, staticFile, } from './components/Media'; +export { LottieAnimation } from './components/Lottie'; // Effect components export { @@ -214,6 +215,7 @@ export type { AudioProps, ImageProps, TextProps, + LottieAnimationProps, SpringConfig, InterpolateOptions, EasingFunction, diff --git a/src/lib/remotion/components/Lottie.tsx b/src/lib/remotion/components/Lottie.tsx new file mode 100644 index 0000000..3ce0ba3 --- /dev/null +++ b/src/lib/remotion/components/Lottie.tsx @@ -0,0 +1,127 @@ +'use client'; + +import React, { useEffect, useRef, useState } from 'react'; +import lottie, { AnimationItem, AnimationConfigWithData, AnimationConfigWithPath } from 'lottie-web'; +import { useCurrentFrame, useVideoConfig } from '../core/context'; +import { useRelativeCurrentFrame } from './Sequence'; +import { LottieAnimationProps } from '../core/types'; + +/** + * LottieAnimation Component + * + * Renders a Lottie animation synchronized with the MotionForge frame system. + * This component is deterministic and does not use requestAnimationFrame for playback. + */ +export const LottieAnimation: React.FC = ({ + src, + frameStart = 0, + frameEnd, + playbackRate = 1, + loop = false, + width, + height, + style, + className, +}) => { + const containerRef = useRef(null); + const animationRef = useRef(null); + + // Use relative frame if inside a Sequence, otherwise use absolute frame + const relativeFrame = useRelativeCurrentFrame(); + const absoluteFrame = useCurrentFrame(); + const currentFrame = relativeFrame !== null ? relativeFrame : absoluteFrame; + + const [isLoaded, setIsLoaded] = useState(false); + + // Initialize Lottie instance + useEffect(() => { + if (!containerRef.current) return; + + // Clear container to prevent duplicate renders + containerRef.current.innerHTML = ''; + + const commonConfig = { + container: containerRef.current, + renderer: 'svg' as const, + loop: false, // We control playback manually + autoplay: false, // We control playback manually + }; + + let anim: AnimationItem; + + if (typeof src === 'string') { + anim = lottie.loadAnimation({ + ...commonConfig, + path: src, + } as AnimationConfigWithPath); + } else { + // Create a deep copy of animationData to prevent lottie-web from mutating it + anim = lottie.loadAnimation({ + ...commonConfig, + animationData: JSON.parse(JSON.stringify(src)), + } as AnimationConfigWithData); + } + + animationRef.current = anim; + + const handleLoaded = () => { + setIsLoaded(true); + }; + + // Lottie emits DOMLoaded when the SVG elements are added to the DOM + anim.addEventListener('DOMLoaded', handleLoaded); + + return () => { + anim.removeEventListener('DOMLoaded', handleLoaded); + anim.destroy(); + animationRef.current = null; + }; + }, [src]); + + // Synchronize Lottie frame with MotionForge frame + useEffect(() => { + if (!animationRef.current || !isLoaded) return; + + const anim = animationRef.current; + + // totalFrames is the number of frames in the Lottie animation + const totalLottieFrames = anim.totalFrames; + + // Determine the effective end frame for the animation window + const end = frameEnd !== undefined ? frameEnd : totalLottieFrames; + const duration = end - frameStart; + + // Calculate the target frame in the Lottie animation + let lottieFrame = (currentFrame * playbackRate) + frameStart; + + if (loop && duration > 0) { + // Loop within the specified window [frameStart, end] + lottieFrame = frameStart + ((lottieFrame - frameStart) % duration); + } else { + // Clamp to the end frame if not looping + lottieFrame = Math.min(lottieFrame, end); + } + + // Go to the calculated frame and stop (prevent internal playback) + // The second parameter 'true' indicates we are using frame numbers, not time. + anim.goToAndStop(lottieFrame, true); + }, [currentFrame, isLoaded, frameStart, frameEnd, playbackRate, loop]); + + return ( +
+ ); +}; + +export default LottieAnimation; diff --git a/src/lib/remotion/components/Media.tsx b/src/lib/remotion/components/Media.tsx index c081791..0686fc7 100755 --- a/src/lib/remotion/components/Media.tsx +++ b/src/lib/remotion/components/Media.tsx @@ -6,7 +6,7 @@ import { interpolate } from '../utils/animation'; // Absolute Fill - Container component interface AbsoluteFillProps { - children: React.ReactNode; + children?: React.ReactNode; style?: React.CSSProperties; className?: string; } @@ -223,11 +223,6 @@ export const Img: React.FC = ({ }; // StaticFile component - for local static assets -interface StaticFileProps { - src: string; - style?: React.CSSProperties; -} - export const staticFile = (path: string): string => { // In a real implementation, this would resolve to a static file path return `/static/${path}`; diff --git a/src/lib/remotion/components/Sequence.tsx b/src/lib/remotion/components/Sequence.tsx index 16eac52..6226ae9 100755 --- a/src/lib/remotion/components/Sequence.tsx +++ b/src/lib/remotion/components/Sequence.tsx @@ -107,7 +107,7 @@ const SequenceFrameProvider: React.FC = ({ }; // Relative Frame Context -const RelativeFrameContext = createContext(0); +const RelativeFrameContext = createContext(null); export const useRelativeCurrentFrame = () => useContext(RelativeFrameContext); diff --git a/src/lib/remotion/core/types.ts b/src/lib/remotion/core/types.ts index 68d3c6b..1aa2530 100755 --- a/src/lib/remotion/core/types.ts +++ b/src/lib/remotion/core/types.ts @@ -59,6 +59,18 @@ export interface TextProps { children: React.ReactNode; } +export interface LottieAnimationProps { + src: string | object; + frameStart?: number; + frameEnd?: number; + playbackRate?: number; + loop?: boolean; + width?: number | string; + height?: number | string; + style?: React.CSSProperties; + className?: string; +} + export interface SpringConfig { frame: number; fps: number; diff --git a/src/lib/remotion/demo/LottieDemo.tsx b/src/lib/remotion/demo/LottieDemo.tsx new file mode 100644 index 0000000..7b0fba3 --- /dev/null +++ b/src/lib/remotion/demo/LottieDemo.tsx @@ -0,0 +1,73 @@ +'use client'; + +import React from 'react'; +import { AbsoluteFill, Sequence, LottieAnimation, Text } from '../index'; +import sampleLottie from './sample-lottie.json'; + +export const LottieDemo: React.FC = () => { + return ( + +
+

Lottie Integration

+

+ Production-grade Lottie animations with frame-perfect synchronization. +

+
+ +
+ {/* Basic Usage */} +
+
+ +
+ Basic Playback +
+ + {/* Custom Start/End */} +
+
+ +
+ Partial Window (Looping) +
+ + {/* Playback Rate */} +
+
+ +
+ Slow Motion (0.5x) +
+ + {/* Inside a Sequence */} +
+ +
+ +
+ Inside Sequence (Starts at frame 60) +
+
+
+ + +
+ Deterministic Rendering System +
+
+
+ ); +}; + +export default LottieDemo; diff --git a/src/lib/remotion/demo/sample-lottie.json b/src/lib/remotion/demo/sample-lottie.json new file mode 100644 index 0000000..428b4c8 --- /dev/null +++ b/src/lib/remotion/demo/sample-lottie.json @@ -0,0 +1,35 @@ +{ + "v": "5.7.4", + "fr": 30, + "ip": 0, + "op": 60, + "w": 500, + "h": 500, + "nm": "Sample Square", + "ddd": 0, + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 1, + "ty": 1, + "nm": "Square", + "sr": 1, + "ks": { + "o": { "a": 0, "k": 100, "ix": 11 }, + "r": { "a": 1, "k": [{ "i": { "x": [0.833], "y": [0.833] }, "o": { "x": [0.167], "y": [0.167] }, "t": 0, "s": [0] }, { "t": 60, "s": [360] }], "ix": 10 }, + "p": { "a": 1, "k": [{ "i": { "x": 0.833, "y": 0.833 }, "o": { "x": 0.167, "y": 0.167 }, "t": 0, "s": [250, 250, 0], "to": [0, 0, 0], "ti": [0, 0, 0] }, { "t": 60, "s": [250, 250, 0] }], "ix": 2 }, + "a": { "a": 0, "k": [25, 25, 0], "ix": 1 }, + "s": { "a": 1, "k": [{ "i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] }, "o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] }, "t": 0, "s": [100, 100, 100] }, { "i": { "x": [0.833, 0.833, 0.833], "y": [0.833, 0.833, 0.833] }, "o": { "x": [0.167, 0.167, 0.167], "y": [0.167, 0.167, 0.167] }, "t": 30, "s": [200, 200, 100] }, { "t": 60, "s": [100, 100, 100] }], "ix": 6 } + }, + "ao": 0, + "sw": 50, + "sh": 50, + "sc": "#10b981", + "ip": 0, + "op": 60, + "st": 0, + "bm": 0 + } + ] +} diff --git a/src/lib/remotion/index.ts b/src/lib/remotion/index.ts index c73095c..e481f69 100755 --- a/src/lib/remotion/index.ts +++ b/src/lib/remotion/index.ts @@ -6,7 +6,7 @@ * marketing videos, and automated video generation. * * @package MotionForge - * @version 1.2.0 + * @version 1.3.0 * @license MIT */ @@ -47,6 +47,7 @@ export { G, staticFile, } from './components/Media'; +export { LottieAnimation } from './components/Lottie'; // Effect components export { @@ -214,6 +215,7 @@ export type { AudioProps, ImageProps, TextProps, + LottieAnimationProps, SpringConfig, InterpolateOptions, EasingFunction,