Skip to main content

History

Shiam56's avatar
Prompted by Shiam56
9 days ago

Tool used

```tsx import React from "react"; import { AbsoluteFill, useCurrentFrame, useVideoConfig, spring, interpolate, Sequence } from "remotion"; import { evolvePath } from "@remotion/paths"; const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]; const revenue = [8000, 12000, 15000, 11000, 18000, 22000]; const conversionRate = [2.1, 2.8, 3.2, 2.9, 3.8, 4.2]; const CHART_WIDTH = 1400; const CHART_HEIGHT = 600; const BAR_WIDTH = 80; const MAX_REVENUE = 25000; const CHART_LEFT = 200; const CHART_BOTTOM = 900; export const BarLineChart: React.FC = () => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); // Spring configuration for smooth animation const springConfig = { damping: 100, stiffness: 100, mass: 0.8, }; // Calculate line path const barSpacing = CHART_WIDTH / revenue.length; const linePoints = revenue.map((_, i) => { const x = CHART_LEFT + i * barSpacing + barSpacing / 2; const y = CHART_BOTTOM - (conversionRate[i] / 5) * CHART_HEIGHT; return { x, y }; }); // Create SVG path const linePath = linePoints .map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`) .join(" "); // Line animation progress (starts after bars begin, trails behind) const lineProgress = spring({ fps, frame: frame - 20, config: { ...springConfig, damping: 50 }, }); // Get stroke-dasharray and offset for line drawing effect const lineEvolution = linePath ? evolvePath(Math.min(lineProgress, 1), linePath) : { strokeDasharray: "0", strokeDashoffset: 0 }; // Pulse animation for the dot marker const pulse = spring({ fps, frame: frame - 100, config: { damping: 5, stiffness: 150, mass: 0.5 }, }); const dotScale = 1 + pulse * 0.3; // Get current tip position based on progress const getTipPosition = () => { const totalPoints = linePoints.length - 1; const exactIndex = lineProgress * totalPoints; const index = Math.floor(exactIndex); const nextIndex = Math.min(index + 1, totalPoints); const localProgress = exactIndex - index; if (index >= linePoints.length) return linePoints[linePoints.length - 1]; const current = linePoints[index]; const next = linePoints[nextIndex]; return { x: interpolate(localProgress, [0, 1], [current.x, next.x]), y: interpolate(localProgress, [0, 1], [current.y, next.y]), }; }; const tipPosition = getTipPosition(); return ( <AbsoluteFill style={{ backgroundColor: "#1A1A2E", fontFamily: "Inter, sans-serif", }} > <svg width={1920} height={1080} style={{ position: "absolute" }}> {/* Grid lines */} {[0, 0.25, 0.5, 0.75, 1].map((ratio, i) => ( <line key={i} x1={CHART_LEFT} y1={CHART_BOTTOM - ratio * CHART_HEIGHT} x2={CHART_LEFT + CHART_WIDTH} y2={CHART_BOTTOM - ratio * CHART_HEIGHT} stroke="#2A2A4E" strokeWidth={1} /> ))} {/* Y-axis labels */} {[0, 6250, 12500, 18750, 25000].map((val, i) => ( <text key={i} x={CHART_LEFT - 20} y={CHART_BOTTOM - (i / 4) * CHART_HEIGHT + 5} fill="#8888AA" fontSize={16} textAnchor="end" > ${val / 1000}K </text> ))} {/* Conversion rate axis labels */} {[0, 1.25, 2.5, 3.75, 5].map((val, i) => ( <text key={i} x={CHART_LEFT + CHART_WIDTH + 40} y={CHART_BOTTOM - (i / 4) * CHART_HEIGHT + 5} fill="#0B84F3" fontSize={16} textAnchor="start" > {val}% </text> ))} {/* Bars with sequential spring animation */} {revenue.map((rev, i) => { const barStartFrame = i * 12; // Stagger by 12 frames with overlap const barProgress = spring({ fps, frame: frame - barStartFrame, config: springConfig, }); const barHeight = (rev / MAX_REVENUE) * CHART_HEIGHT * barProgress; const x = CHART_LEFT + i * barSpacing + (barSpacing - BAR_WIDTH) / 2; const y = CHART_BOTTOM - barHeight; return ( <Sequence key={i} from={Math.max(0, barStartFrame - 5)}> <rect x={x} y={y} width={BAR_WIDTH} height={barHeight} fill="#3D3D66" rx={4} /> {/* Revenue label */} {barProgress > 0.8 && ( <text x={x + BAR_WIDTH / 2} y={y - 10} fill="#CCCCDD" fontSize={14} textAnchor="middle" opacity={(barProgress - 0.8) * 5} > ${rev / 1000}K </text> )} </Sequence> ); })} {/* X-axis labels */} {months.map((month, i) => { const x = CHART_LEFT + i * barSpacing + barSpacing / 2; return ( <text key={i} x={x} y={CHART_BOTTOM + 30} fill="#8888AA" fontSize={16} textAnchor="middle" > {month} </text> ); })} {/* Conversion rate line with glow effect */} <defs> <filter id="glow" x="-50%" y="-50%" width="200%" height="200%"> <feGaussianBlur stdDeviation="4" result="coloredBlur" /> <feMerge> <feMergeNode in="coloredBlur" /> <feMergeNode in="SourceGraphic" /> </feMerge> </filter> </defs> <path d={linePath} fill="none" stroke="#0B84F3" strokeWidth={3} strokeLinecap="round" filter="url(#glow)" strokeDasharray={lineEvolution.strokeDasharray} strokeDashoffset={lineEvolution.strokeDashoffset} /> {/* Pulsing dot marker at line tip */} {lineProgress > 0 && ( <circle cx={tipPosition.x} cy={tipPosition.y} r={6 * dotScale} fill="#0B84F3" filter="url(#glow)" /> )} </svg> </AbsoluteFill> ); }; ``` Register in your `Root.tsx`: ```tsx <Composition id="BarLineChart" component={BarLineChart} durationInFrames={120} fps={30} width={1920} height={1080} /> ```

Model used

Opus 4.5

Prompt

Create a 1920x1080 dark-themed (#1A1A2E) composition called 'BarLineChart' with a combination chart showing monthly sales data — bars for revenue ($8K, $12K, $15K, $11K, $18K, $22K for Jan-Jun) that grow upward from the baseline, overlaid with a blue (#0B84F3) line tracking conversion rate (2.1%, 2.8%, 3.2%, 2.9%, 3.8%, 4.2%) that draws progressively with a glowing effect, bars animate sequentially with slight overlap while the line follows behind, include axis labels and a pulsing dot marker at the line tip, smooth spring-based timing over 120 frames at 30fps — use remotion-best-practices skill.