-
Notifications
You must be signed in to change notification settings - Fork 0
Lottie Animation Integration #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Version mismatch with motionforge's own The template pins 🤖 Prompt for AI Agents |
||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^20", | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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 | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incorrect date: The changelog entry date appears to be two years in the past. The PR was created on 2026-02-15. Proposed fix-## [1.3.0] - 2024-02-25
+## [1.3.0] - 2026-02-15📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
|
|
||||||
| ### Added | ||||||
| - Production-grade Lottie animation support with `<LottieAnimation />` | ||||||
| - 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 | ||||||
|
|
||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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" | ||
| } | ||
|
Comment on lines
+91
to
93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Check the motionforge package.json dependencies
cat -n packages/motionforge/package.json | grep -A 5 -B 5 "lottie-web"Repository: codedbytahir/motionforge Length of output: 272 🏁 Script executed: # Check the CLI template for lottie-web version
cat -n shared/package.json.template | grep -A 5 -B 5 "lottie-web"Repository: codedbytahir/motionforge Length of output: 128 🏁 Script executed: # Find if lottie-web or LottieAnimation is used in the codebase
rg -i "lottie" --type ts --type tsx --type js --type jsxRepository: codedbytahir/motionforge Length of output: 95 🌐 Web query:
💡 Result: The latest Source: npm package page for References: Citations: 🏁 Script executed: # Search for lottie-web usage without type restrictions
rg "lottie-web" -A 3 -B 1Repository: codedbytahir/motionforge Length of output: 3841 🏁 Script executed: # Search for lottie or LottieAnimation more broadly
rg "lottie" -i -A 2 -B 1Repository: codedbytahir/motionforge Length of output: 22590 🏁 Script executed: # Find if there's a template file with a different name/path
fd "package.*template" -type fRepository: codedbytahir/motionforge Length of output: 238 🏁 Script executed: # List files in shared directory to see what exists
ls -la shared/ 2>/dev/null || echo "shared directory not found"Repository: codedbytahir/motionforge Length of output: 94 Update the CLI template to use The template at Regarding the optional dependency suggestion: 🤖 Prompt for AI Agents |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<LottieAnimationProps> = ({ | ||
| src, | ||
| frameStart = 0, | ||
| frameEnd, | ||
| playbackRate = 1, | ||
| loop = false, | ||
| width, | ||
| height, | ||
| style, | ||
| className, | ||
| }) => { | ||
| const containerRef = useRef<HTMLDivElement>(null); | ||
| const animationRef = useRef<AnimationItem | null>(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 ( | ||
| <div | ||
| ref={containerRef} | ||
| className={className} | ||
| style={{ | ||
| width: width || '100%', | ||
| height: height || '100%', | ||
| display: 'flex', | ||
| alignItems: 'center', | ||
| justifyContent: 'center', | ||
| overflow: 'hidden', | ||
| ...style, | ||
| }} | ||
| /> | ||
| ); | ||
| }; | ||
|
|
||
| export default LottieAnimation; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -107,7 +107,7 @@ const SequenceFrameProvider: React.FC<SequenceFrameProviderProps> = ({ | |
| }; | ||
|
|
||
| // Relative Frame Context | ||
| const RelativeFrameContext = createContext<number>(0); | ||
| const RelativeFrameContext = createContext<number | null>(null); | ||
|
|
||
| export const useRelativeCurrentFrame = () => useContext(RelativeFrameContext); | ||
|
Comment on lines
+110
to
112
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's examine the Sequence.tsx file around the lines mentioned
cat -n packages/motionforge/src/components/Sequence.tsx | sed -n '100,125p'Repository: codedbytahir/motionforge Length of output: 946 🏁 Script executed: # Search for all usages of useRelativeCurrentFrame
rg -n 'useRelativeCurrentFrame' --type=ts --type=tsx -B2 -A4Repository: codedbytahir/motionforge Length of output: 95 🏁 Script executed: # Also search in JS/JSX files in case there are any
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) | xargs grep -l 'useRelativeCurrentFrame' 2>/dev/null | head -20Repository: codedbytahir/motionforge Length of output: 391 🏁 Script executed: # Check the Lottie.tsx file where useRelativeCurrentFrame is likely used
cat -n packages/motionforge/src/components/Lottie.tsxRepository: codedbytahir/motionforge Length of output: 4820 🏁 Script executed: # Check the index.ts files to see how it's exported
cat -n packages/motionforge/src/index.tsRepository: codedbytahir/motionforge Length of output: 5664 🏁 Script executed: # Get more detailed context around usages
rg 'useRelativeCurrentFrame' --type-add 'tsx:*.tsx' --type-add 'ts:*.ts' -B3 -A3 packages/motionforge/src/ 2>/dev/null || rg 'useRelativeCurrentFrame' -B3 -A3 packages/motionforge/src/Repository: codedbytahir/motionforge Length of output: 2347 🏁 Script executed: # Check if there are other usages in the src/lib/remotion directories
cat -n src/lib/remotion/components/Lottie.tsx 2>/dev/null | head -50Repository: codedbytahir/motionforge Length of output: 2005 🏁 Script executed: # Check the other Sequence file
cat -n src/lib/remotion/components/Sequence.tsx 2>/dev/null | head -50Repository: codedbytahir/motionforge Length of output: 1667 🏁 Script executed: # Search for any other usages in the entire src directory
rg 'useRelativeCurrentFrame' src/ 2>/dev/null || find src -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -exec grep -l 'useRelativeCurrentFrame' {} \;Repository: codedbytahir/motionforge Length of output: 423
The type change from 🤖 Prompt for AI Agents |
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
External URL in a starter template is a reliability risk.
The
srcpoints tohttps://assets10.lottiefiles.com/packages/lf20_m6cu96.json, which is a third-party CDN. If this asset is removed or the CDN is unreachable, new users will see a broken animation on first run — a poor out-of-box experience.Consider bundling a local sample JSON (similar to the
sample-lottie.jsonalready added in this PR for the demo) and referencing it via a static file path or import.Proposed fix: use a bundled local asset
You'd also need to include a copy of
sample-lottie.json(or similar) in the template directory.🤖 Prompt for AI Agents