video-coder

Parent div vs motion.div example:

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "video-coder" with this command: npx skills add outscal/video-generator/outscal-video-generator-video-coder

Video Coder

Parent div vs motion.div example:

{/* PARENT DIV: positioning, static transforms (rotation, flip), z-index */} <div className="absolute top-[300px] left-[500px] -translate-x-1/2 -translate-y-1/2 z-[10]" style={{ transform: 'rotate(45deg) scaleX(-1)' }}

{/* MOTION.DIV: animations only (opacity, scale, movement) */} <motion.div initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} transition={{ duration: 0.5 }}

&#x3C;svg>...&#x3C;/svg>

</motion.div> </div>

Property Where to Apply

top , left , z-index

Parent div (className)

rotation , flipX , flipY

Parent div (style.transform)

opacity , scale animations motion.div

x , y movement animations motion.div

export default Scene{N};

Where {N} is the scene number (e.g., Scene0, Scene1, Scene2)

currentTime is the global value of time with respect to the video start. </export-pattern>

</required-structure>


<sub-components>

Sub-Components (CRITICAL)

All sub-components MUST use React.memo and be defined at module level (outside the main Scene component).

<react-memo>

Why React.memo is Required

  • Video components re-render 60 times per second as currentTime changes
  • Without React.memo, sub-components re-render unnecessarily causing animation jitter
  • Module-level definitions ensure stable references across renders </react-memo>

<sub-component-pattern> // CORRECT: Module-level with React.memo + wrapper div for positioning const TreeNode = React.memo(({ value, position, isVisible }: { value: string; position: { x: number; y: number }; isVisible: boolean; }) => ( <div className="absolute -translate-x-1/2 -translate-y-1/2" style={{ left: ${position.x}px, top: ${position.y}px }}

&#x3C;motion.div
  animate={isVisible ? "visible" : "hidden"}>
  {value}
&#x3C;/motion.div>

</div> )); // WRONG: Defined inside component (causes jitter) export default function Scene0({ currentTime }: SceneProps) { // ❌ Never define components here const TreeNode = ({ value }) => <div>{value}</div>; } </sub-component-pattern>

<module-level-definitions>

What Goes at Module Level (Outside Component)

  1. Sub-components - Always wrapped with React.memo
  2. Wrapper div - For positioning and other (absolute, translate, left/top style, etc.)
  3. Animation variants - Objects defining animation states
  4. Static data - Positions, configurations that don't change
// Animation variants at module level
const fadeVariants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1, transition: { duration: 0.5 } }
};

// Static positions at module level
const nodePositions = {
  node1: { x: 576, y: 540 },
  node2: { x: 1344, y: 540 }
};

// Sub-component at module level with React.memo + wrapper div
const InfoCard = React.memo(({ title, position, isVisible }: { title: string; position: { x: number; y: number }; isVisible: boolean }) => (
  &#x3C;div
    className="absolute -translate-x-1/2 -translate-y-1/2"
    style={{ left: `${position.x}px`, top: `${position.y}px` }}
  >
    &#x3C;motion.div variants={fadeVariants} animate={isVisible ? "visible" : "hidden"}>
      {title}
    &#x3C;/motion.div>
  &#x3C;/div>
));

import React, { useMemo } from 'react';
import { motion } from 'framer-motion';

interface SceneProps {
  currentTime: number;
}

// Animation variants at module level
const nodeVariants = {
  hidden: { scale: 0, opacity: 0 },
  visible: { scale: 1, opacity: 1, transition: { duration: 0.4 } }
};

// Static data at module level
const nodePositions = {
  node1: { x: 576, y: 540 },
  node2: { x: 1344, y: 540 }
};

// Sub-component at module level with React.memo
const TreeNode = React.memo(({
  value,
  position,
  isVisible
}: {
  value: string;
  position: { x: number; y: number };
  isVisible: boolean;
}) => (
  &#x3C;div
    className="absolute -translate-x-1/2 -translate-y-1/2"
    style={{ left: `${position.x}px`, top: `${position.y}px` }}
  >
    &#x3C;motion.div
      variants={nodeVariants}
      initial="hidden"
      animate={isVisible ? "visible" : "hidden"}
      className="w-20 h-20 rounded-full bg-white flex items-center justify-center"
    >
      {value}
    &#x3C;/motion.div>
  &#x3C;/div>
));

// Main Scene component with React.memo
const Scene0 = React.memo(function Scene0({ currentTime }: SceneProps) {
  // Threshold-based state updates
  const states = useMemo(() => ({
    showNode1: currentTime >= 1000,
    showNode2: currentTime >= 2000,
  }), [Math.floor(currentTime / 42)]);

  return (
    &#x3C;div className="relative w-full h-full bg-gray-900">
      &#x3C;TreeNode value="A" position={nodePositions.node1} isVisible={states.showNode1} />
      &#x3C;TreeNode value="B" position={nodePositions.node2} isVisible={states.showNode2} />
    &#x3C;/div>
  );
});

export default Scene0;

What They Do

Let you set custom sizes, spacing, colors, borders, radii, typography, and positioning instantly.

Examples

- w-[37px]

- h-[3.5rem]

- p-[18px]

- m-[2.75rem]

- bg-[#1a73e8]

- text-[22px]

- border-[3px]

- rounded-[14px]

- gap-[22px]

- top-[42px]

- z-[25]

CRITICAL: Follow these patterns to prevent animation jittering and re-rendering issues.

React video components re-render up to 60 times per second. Unstable references cause animations to restart, creating visual jitter.

Define sub-components, animation variants, and static data outside the parent component for stable references.

// Animation variants at module level
const nodeVariants = { hidden: { scale: 0 }, visible: { scale: 1 } };

// Static data at module level
const nodePositions = { node1: { x: 576, y: 540 }, node2: { x: 740, y: 540 } };

// Sub-component at module level (see &#x3C;complete-example> for full pattern)
const TreeNode = React.memo(({ value, position, isVisible }) => (
  &#x3C;div style={{ left: `${position.x}px`, top: `${position.y}px` }}>
    &#x3C;motion.div animate={isVisible ? "visible" : "hidden"}>{value}&#x3C;/motion.div>
  &#x3C;/div>
));

See &#x3C;complete-example>
 above for the full implementation pattern.

Update states every 42ms using Math.floor(currentTime / 42)
 to prevent excessive re-renders while matching 24fps video output.

// State updates inside components
const states = useMemo(() => ({
  showTitle: currentTime >= 1000,
  showGrid: currentTime >= 2000,
  fadeOut: currentTime >= 9000
}), [Math.floor(currentTime / 42)]);

// Computed collections inside components
const visibleItems = useMemo(() => {
  const visible = new Set&#x3C;string>();
  if (currentTime >= 1000) visible.add('item1');
  if (currentTime >= 2000) visible.add('item2');
  return visible;
}, [Math.floor(currentTime / 42)]);

// Static data created once at mount
const particles = useMemo(() =>
  Array.from({ length: 40 }, () => ({
    x: Math.random() * 100,
    y: Math.random() * 100
  })),
  [] // Empty deps = created once
);

Pass all dependencies as explicit props for React.memo
 to work correctly.

const TreeNode = React.memo(({
  value,
  position,
  showTree  // Explicit prop, not derived from currentTime inside
}: {
  value: string;
  position: { x: number; y: number };
  showTree: boolean;
}) => (
  &#x3C;div
    className="absolute -translate-x-1/2 -translate-y-1/2"
    style={{ left: `${position.x}px`, top: `${position.y}px` }}
  >
    &#x3C;motion.div animate={showTree ? "visible" : "hidden"}>
      {value}
    &#x3C;/motion.div>
  &#x3C;/div>
));

// In parent: derive state, pass as prop
&#x3C;TreeNode value="50" position={{ x: 960, y: 540 }} showTree={states.showTree} />

FOR SHAPES/TEXT/ICONS:
Position: Always refers to element's CENTER point

FOR PATHS:
All coordinates are ABSOLUTE screen positions
No position/size fields needed (implied by path coordinates)

ROTATION
0° = pointing up (↑)
90° = pointing right (→)
180° = pointing down (↓)
270° = pointing left (←)

Positive values = clockwise rotation
Negative values = counter-clockwise (-90° same as 270°)

EXAMPLE (1920×1080 viewport)
Screen center:        x = 960,  y = 540
Top-center:           x = 960,  y = 100
Bottom-left quadrant: x = 480,  y = 810
Right edge center:    x = 1820, y = 540

Position at any pixel value using the same pattern:

{/* Content layer - Landscape center: 540px, 960px */}

Be thorough in studying any animation pattern you're using in your scene.

Type
Description
Use Case

Tween
Duration-based, precise timing
Coordinated animations, sync with audio

Spring
Physics-based, bounce/elasticity
Interactive UI, natural motion

Inertia
Momentum-based deceleration
Drag interactions, swipe gestures

transition={{
  duration?: number,        // Seconds (default: 0.3)
  ease?: string | array,    // Easing function (default: "easeInOut")
  delay?: number,           // Delay in seconds
  repeat?: number,          // Number of repeats (Infinity for loop)
  repeatType?: "loop" | "reverse" | "mirror",
  times?: number[],         // Keyframe timing [0, 0.5, 1]
}}

Ease
Behavior
Use Case

linear

Constant speed
Mechanical motion, loading indicators

easeIn

Slow → fast
Exit animations, falling objects

easeOut

Fast → slow
Entrances, coming to rest

easeInOut

Slow → fast → slow
Default for most UI animations

circIn/Out/InOut

Sharper circular curve
Snappy, aggressive motion

backIn

Pulls back, then forward
Anticipation effects

backOut

Overshoots, then settles
Bouncy clicks, attention-grabbing

backInOut

Both effects combined
Playful, game UI

anticipate

Dramatic pullback
Hero entrances, launch effects

steps(n)

Discrete steps
Pixel art, frame-by-frame

Note: Cannot mix bounce
 with stiffness
/damping
/mass
.

// Snappy
transition={{ type: "spring", stiffness: 400, damping: 30 }}

// Soft
transition={{ type: "spring", stiffness: 60, damping: 10 }}

&#x3C;motion.div
  drag
  dragConstraints={{ left: 0, right: 400 }}
  dragTransition={{
    power?: number,           // Deceleration rate (default: 0.8)
    timeConstant?: number,    // Duration in ms (default: 700)
    bounceStiffness?: number, // Boundary spring (default: 500)
    bounceDamping?: number,   // Boundary damping (default: 10)
  }}
/>

// Static orientation (asset facing a direction) - use wrapper div

// Static orientation with flip

Animated rotation (rotation that changes over time):

// Clockwise rotation (positive degrees)
&#x3C;motion.div animate={{ rotate: 90 }} transition={{ duration: 1 }} />

// Anti-clockwise rotation (negative degrees)
&#x3C;motion.div animate={{ rotate: -90 }} transition={{ duration: 1 }} />

// Continuous clockwise spin
&#x3C;motion.div
animate={{ rotate: 360 }}
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
/>

// Continuous anti-clockwise spin
&#x3C;motion.div
animate={{ rotate: -360 }}
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
/>

// Custom pivot point
&#x3C;motion.div
style={{ transformOrigin: "top left" }}
animate={{ rotate: 45 }}
/>

- rotate
 - 2D rotation in degrees (positive = clockwise, negative = anti-clockwise)

- rotateX
, rotateY
 - 2D axis rotation

- transformOrigin
 - pivot point (default: "center"
)

For animating elements along SVG paths, see the dedicated path-following.md.

Important: When a path has both path-draw
 and follow-path
 animations, apply the same easing to both to keep them synchronized.

transition={{
  x: { type: "spring", stiffness: 200 },
  opacity: { duration: 0.5, ease: "easeOut" },
  scale: { type: "spring", bounce: 0.6 }
}}

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

shorts-script-personality

No summary provided by upstream source.

Repository SourceNeeds Review
General

video-creator

No summary provided by upstream source.

Repository SourceNeeds Review
General

script-writer-personality

No summary provided by upstream source.

Repository SourceNeeds Review