mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(videos): add all 5 Promo Teaser scene components
This commit is contained in:
@@ -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 (
|
||||||
|
<AbsoluteFill style={{ opacity: fadeIn }}>
|
||||||
|
{BLOBS.map((blob, i) => (
|
||||||
|
<GradientBlob key={`ambient-${i}`} config={blob} duration={600} />
|
||||||
|
))}
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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 (
|
||||||
|
<AbsoluteFill
|
||||||
|
style={{
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
opacity: fadeOut,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
transform: `scale(${pillScale})`,
|
||||||
|
padding: "14px 36px",
|
||||||
|
borderRadius: 50,
|
||||||
|
background: `linear-gradient(135deg, ${COLOR.accent}, ${COLOR.accentHover})`,
|
||||||
|
boxShadow: `0 0 30px rgba(245, 158, 11, ${glowOpacity})`,
|
||||||
|
color: "white",
|
||||||
|
fontFamily: FONT.heading,
|
||||||
|
fontWeight: 700,
|
||||||
|
fontSize: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Get it free
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ height: 16 }} />
|
||||||
|
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily: FONT.mono,
|
||||||
|
fontSize: 16,
|
||||||
|
color: "white",
|
||||||
|
opacity: urlOpacity,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
snapotter.com
|
||||||
|
</span>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import { AbsoluteFill } from "remotion";
|
||||||
|
import { LogoReveal } from "@/components/LogoReveal";
|
||||||
|
|
||||||
|
export const LogoRevealScene: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<AbsoluteFill>
|
||||||
|
<LogoReveal
|
||||||
|
convergeFrame={0}
|
||||||
|
burstFrame={30}
|
||||||
|
logoFrame={30}
|
||||||
|
textFrame={50}
|
||||||
|
taglineFrame={70}
|
||||||
|
logoSize={80}
|
||||||
|
/>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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 (
|
||||||
|
<AbsoluteFill style={{ justifyContent: "center", alignItems: "center" }}>
|
||||||
|
{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 (
|
||||||
|
<AbsoluteFill key={p.number} style={{ justifyContent: "center", alignItems: "center" }}>
|
||||||
|
{i > 0 && (
|
||||||
|
<AbsoluteFill style={{ backgroundColor: COLOR.dark, opacity: flashOpacity }} />
|
||||||
|
)}
|
||||||
|
{visible && (
|
||||||
|
<NumberPunch
|
||||||
|
number={p.number}
|
||||||
|
descriptor={p.descriptor}
|
||||||
|
enterFrame={p.frame}
|
||||||
|
numberSize={p.size}
|
||||||
|
shakeIntensity={p.shake}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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 (
|
||||||
|
<AbsoluteFill>
|
||||||
|
<RotatingTaglines
|
||||||
|
lines={TAGLINES}
|
||||||
|
startFrame={0}
|
||||||
|
framesPerLine={25}
|
||||||
|
fontSize={40}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
</AbsoluteFill>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user