diff --git a/apps/videos/src/components/FeaturePill.tsx b/apps/videos/src/components/FeaturePill.tsx new file mode 100644 index 00000000..d2b751f9 --- /dev/null +++ b/apps/videos/src/components/FeaturePill.tsx @@ -0,0 +1,55 @@ +import type React from "react"; +import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion"; +import { COLOR } from "@/lib/colors"; +import { FONT } from "@/lib/fonts"; +import { SPRING } from "@/lib/motion"; + +export const FeaturePill: React.FC<{ + label: string; + enterFrame: number; + targetX: number; + targetY: number; + fromDirection?: "left" | "right" | "top" | "bottom"; +}> = ({ label, enterFrame, targetX, targetY, fromDirection = "left" }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const progress = spring({ + frame: frame - enterFrame, + fps, + config: SPRING.popIn, + }); + + const offscreen = { + left: { x: -300, y: targetY }, + right: { x: 1400, y: targetY }, + top: { x: targetX, y: -100 }, + bottom: { x: targetX, y: 1200 }, + }[fromDirection]; + + const x = interpolate(progress, [0, 1], [offscreen.x, targetX]); + const y = interpolate(progress, [0, 1], [offscreen.y, targetY]); + const scale = interpolate(progress, [0, 1], [0.8, 1]); + + return ( +
+ {label} +
+ ); +}; diff --git a/apps/videos/src/components/NumberPunch.tsx b/apps/videos/src/components/NumberPunch.tsx new file mode 100644 index 00000000..57039487 --- /dev/null +++ b/apps/videos/src/components/NumberPunch.tsx @@ -0,0 +1,72 @@ +import type React from "react"; +import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion"; +import { COLOR } from "@/lib/colors"; +import { TEXT } from "@/lib/fonts"; +import { SPRING } from "@/lib/motion"; + +export const NumberPunch: React.FC<{ + number: string; + descriptor: string; + enterFrame: number; + numberSize?: number; + shakeIntensity?: number; +}> = ({ number, descriptor, enterFrame, numberSize = 120, shakeIntensity = 2 }) => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const elapsed = frame - enterFrame; + const scaleSpring = spring({ frame: elapsed, fps, config: SPRING.settle }); + const scale = interpolate(scaleSpring, [0, 1], [1.5, 1]); + + const shakeDecay = interpolate(elapsed, [0, 12], [1, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const shakeX = Math.sin(elapsed * 3) * shakeIntensity * shakeDecay; + const shakeY = Math.cos(elapsed * 4) * shakeIntensity * shakeDecay; + + const descOpacity = interpolate(elapsed, [8, 18], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + const numberOpacity = interpolate(elapsed, [0, 4], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + return ( +
+ + {number} + + + {descriptor} + +
+ ); +}; diff --git a/apps/videos/src/components/ProgressBar.tsx b/apps/videos/src/components/ProgressBar.tsx new file mode 100644 index 00000000..adcccfa8 --- /dev/null +++ b/apps/videos/src/components/ProgressBar.tsx @@ -0,0 +1,50 @@ +import type React from "react"; +import { interpolate, useCurrentFrame } from "remotion"; +import { EASE } from "@/lib/motion"; + +export const ProgressBar: React.FC<{ + startFrame: number; + duration: number; + width?: number; + height?: number; + color?: string; + bgColor?: string; + style?: React.CSSProperties; +}> = ({ + startFrame, + duration, + width = 200, + height = 6, + color = "#3b82f6", + bgColor = "rgba(255,255,255,0.1)", + style, +}) => { + const frame = useCurrentFrame(); + const progress = interpolate(frame, [startFrame, startFrame + duration], [0, 100], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + easing: EASE.smooth, + }); + + return ( +
+
+
+ ); +};