mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(videos): add RotatingTaglines and GradientMesh components
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import type React from "react";
|
||||
import type { CSSProperties } from "react";
|
||||
import { AbsoluteFill, interpolate, useCurrentFrame } from "remotion";
|
||||
import { GradientBlob } from "@/components/GradientBlob";
|
||||
|
||||
interface BlobDef {
|
||||
color: string;
|
||||
radius: number;
|
||||
cx: number;
|
||||
cy: number;
|
||||
a: number;
|
||||
b: number;
|
||||
phaseX: number;
|
||||
phaseY: number;
|
||||
amplitudeX: number;
|
||||
amplitudeY: number;
|
||||
}
|
||||
|
||||
export const GradientMesh: React.FC<{
|
||||
blobs: BlobDef[];
|
||||
duration: number;
|
||||
baseOpacity?: number;
|
||||
opacityRange?: [number, number];
|
||||
opacityFrameRange?: [number, number];
|
||||
style?: CSSProperties;
|
||||
}> = ({ blobs, duration, baseOpacity, opacityRange, opacityFrameRange, style }) => {
|
||||
const frame = useCurrentFrame();
|
||||
let containerOpacity = baseOpacity ?? 1;
|
||||
|
||||
if (opacityRange && opacityFrameRange) {
|
||||
containerOpacity = interpolate(frame, opacityFrameRange, opacityRange, {
|
||||
extrapolateLeft: "clamp",
|
||||
extrapolateRight: "clamp",
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<AbsoluteFill style={{ opacity: containerOpacity, ...style }}>
|
||||
{blobs.map((blob, i) => (
|
||||
<GradientBlob key={`mesh-blob-${i}`} config={blob} duration={duration} />
|
||||
))}
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
import type React from "react";
|
||||
import { interpolate, useCurrentFrame } from "remotion";
|
||||
import { COLOR } from "@/lib/colors";
|
||||
import { FONT } from "@/lib/fonts";
|
||||
import { EASE } from "@/lib/motion";
|
||||
|
||||
export const RotatingTaglines: React.FC<{
|
||||
lines: string[];
|
||||
startFrame: number;
|
||||
framesPerLine: number;
|
||||
fontSize?: number;
|
||||
color?: string;
|
||||
}> = ({ lines, startFrame, framesPerLine, fontSize = 36, color = COLOR.accent }) => {
|
||||
const frame = useCurrentFrame();
|
||||
const elapsed = frame - startFrame;
|
||||
if (elapsed < 0) return null;
|
||||
|
||||
const lineIndex = Math.floor(elapsed / framesPerLine);
|
||||
const lineProgress = (elapsed % framesPerLine) / framesPerLine;
|
||||
if (lineIndex >= lines.length) return null;
|
||||
|
||||
const enterDuration = 0.2;
|
||||
const holdEnd = 0.7;
|
||||
const exitEnd = 1.0;
|
||||
|
||||
const opacity =
|
||||
lineProgress < enterDuration
|
||||
? interpolate(lineProgress, [0, enterDuration], [0, 1], { easing: EASE.enter })
|
||||
: lineProgress < holdEnd
|
||||
? 1
|
||||
: interpolate(lineProgress, [holdEnd, exitEnd], [1, 0], { easing: EASE.exit });
|
||||
|
||||
const translateY =
|
||||
lineProgress < enterDuration
|
||||
? interpolate(lineProgress, [0, enterDuration], [30, 0], { easing: EASE.enter })
|
||||
: lineProgress > holdEnd
|
||||
? interpolate(lineProgress, [holdEnd, exitEnd], [0, -20], { easing: EASE.exit })
|
||||
: 0;
|
||||
|
||||
const glowOpacity = interpolate(Math.sin(frame * 0.08), [-1, 1], [0.03, 0.08]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: 400,
|
||||
height: 100,
|
||||
borderRadius: "50%",
|
||||
background: `radial-gradient(ellipse, ${COLOR.accent} 0%, transparent 70%)`,
|
||||
opacity: glowOpacity,
|
||||
filter: "blur(40px)",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
fontFamily: FONT.body,
|
||||
fontWeight: 500,
|
||||
fontSize,
|
||||
color,
|
||||
opacity,
|
||||
transform: `translateY(${translateY}px)`,
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
{lines[lineIndex]}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user