diff --git a/apps/videos/src/compositions/promo-teaser/scenes/AmbientOpenScene.tsx b/apps/videos/src/compositions/promo-teaser/scenes/AmbientOpenScene.tsx new file mode 100644 index 00000000..0d2d80de --- /dev/null +++ b/apps/videos/src/compositions/promo-teaser/scenes/AmbientOpenScene.tsx @@ -0,0 +1,58 @@ +import type React from "react"; +import { AbsoluteFill, interpolate, useCurrentFrame } from "remotion"; +import { GradientBlob } from "@/components/GradientBlob"; + +const BLOBS = [ + { + color: "#f59e0b", + radius: 200, + cx: 300, + cy: 400, + a: 1, + b: 2, + phaseX: 0, + phaseY: 0, + amplitudeX: 60, + amplitudeY: 40, + }, + { + color: "#f97316", + radius: 150, + cx: 750, + cy: 250, + a: 2, + b: 1, + phaseX: 1.2, + phaseY: 0.8, + amplitudeX: 50, + amplitudeY: 60, + }, + { + color: "#d97706", + radius: 180, + cx: 540, + cy: 700, + a: 1, + b: 1, + phaseX: 2.4, + phaseY: 1.6, + amplitudeX: 70, + amplitudeY: 50, + }, +]; + +export const AmbientOpenScene: React.FC = () => { + const frame = useCurrentFrame(); + const fadeIn = interpolate(frame, [0, 45], [0, 1], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + + return ( + + {BLOBS.map((blob, i) => ( + + ))} + + ); +}; diff --git a/apps/videos/src/compositions/promo-teaser/scenes/CTAScene.tsx b/apps/videos/src/compositions/promo-teaser/scenes/CTAScene.tsx new file mode 100644 index 00000000..63f7124a --- /dev/null +++ b/apps/videos/src/compositions/promo-teaser/scenes/CTAScene.tsx @@ -0,0 +1,60 @@ +import type React from "react"; +import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion"; +import { COLOR } from "@/lib/colors"; +import { FONT } from "@/lib/fonts"; +import { SPRING } from "@/lib/motion"; + +export const CTAScene: React.FC = () => { + const frame = useCurrentFrame(); + const { fps } = useVideoConfig(); + + const pillScale = spring({ frame, fps, config: SPRING.popIn }); + const urlOpacity = interpolate(frame, [15, 25], [0, 0.7], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const fadeOut = interpolate(frame, [60, 90], [1, 0], { + extrapolateLeft: "clamp", + extrapolateRight: "clamp", + }); + const glowOpacity = interpolate(Math.sin(frame * 0.1), [-1, 1], [0.3, 0.6]); + + return ( + +
+ Get it free +
+ +
+ + + snapotter.com + + + ); +}; diff --git a/apps/videos/src/compositions/promo-teaser/scenes/LogoRevealScene.tsx b/apps/videos/src/compositions/promo-teaser/scenes/LogoRevealScene.tsx new file mode 100644 index 00000000..f7d7fc93 --- /dev/null +++ b/apps/videos/src/compositions/promo-teaser/scenes/LogoRevealScene.tsx @@ -0,0 +1,18 @@ +import type React from "react"; +import { AbsoluteFill } from "remotion"; +import { LogoReveal } from "@/components/LogoReveal"; + +export const LogoRevealScene: React.FC = () => { + return ( + + + + ); +}; diff --git a/apps/videos/src/compositions/promo-teaser/scenes/NumberPunchScene.tsx b/apps/videos/src/compositions/promo-teaser/scenes/NumberPunchScene.tsx new file mode 100644 index 00000000..3fcb9c95 --- /dev/null +++ b/apps/videos/src/compositions/promo-teaser/scenes/NumberPunchScene.tsx @@ -0,0 +1,45 @@ +import type React from "react"; +import { AbsoluteFill, interpolate, useCurrentFrame } from "remotion"; +import { NumberPunch } from "@/components/NumberPunch"; +import { COLOR } from "@/lib/colors"; + +const PUNCHES = [ + { number: "49", descriptor: "tools", frame: 0, size: 120, shake: 2 }, + { number: "15", descriptor: "AI models", frame: 37, size: 120, shake: 2 }, + { number: "55+", descriptor: "formats", frame: 74, size: 120, shake: 2 }, + { number: "1", descriptor: "container", frame: 111, size: 144, shake: 3 }, +]; + +export const NumberPunchScene: React.FC = () => { + const frame = useCurrentFrame(); + + return ( + + {PUNCHES.map((p, i) => { + const visible = frame >= p.frame && frame < p.frame + 37; + const flashStart = p.frame - 2; + const flashOpacity = + i > 0 && frame >= flashStart && frame < flashStart + 2 + ? interpolate(frame, [flashStart, flashStart + 2], [1, 0]) + : 0; + + return ( + + {i > 0 && ( + + )} + {visible && ( + + )} + + ); + })} + + ); +}; diff --git a/apps/videos/src/compositions/promo-teaser/scenes/TaglineCascadeScene.tsx b/apps/videos/src/compositions/promo-teaser/scenes/TaglineCascadeScene.tsx new file mode 100644 index 00000000..f7b127c0 --- /dev/null +++ b/apps/videos/src/compositions/promo-teaser/scenes/TaglineCascadeScene.tsx @@ -0,0 +1,26 @@ +import type React from "react"; +import { AbsoluteFill } from "remotion"; +import { RotatingTaglines } from "@/components/RotatingTaglines"; + +const TAGLINES = [ + "No signups.", + "No uploads.", + "No limits.", + "Free forever.", + "Open source.", + "Fully offline.", +]; + +export const TaglineCascadeScene: React.FC = () => { + return ( + + + + ); +};