mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(videos): add Terminal component with typing + output animation
This commit is contained in:
@@ -0,0 +1,94 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
|
||||||
|
import { AppWindow } from "@/components/AppWindow";
|
||||||
|
import { TypeWriter } from "@/components/TypeWriter";
|
||||||
|
import { SPRING } from "@/lib/motion";
|
||||||
|
|
||||||
|
interface OutputLine {
|
||||||
|
text: string;
|
||||||
|
color: string;
|
||||||
|
delay: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Terminal: React.FC<{
|
||||||
|
command: { text: string; color: string }[];
|
||||||
|
commandStartFrame: number;
|
||||||
|
commandSpeed?: number;
|
||||||
|
outputLines?: OutputLine[];
|
||||||
|
outputStartFrame: number;
|
||||||
|
outputStagger?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
enterFrame?: number;
|
||||||
|
}> = ({
|
||||||
|
command,
|
||||||
|
commandStartFrame,
|
||||||
|
commandSpeed = 2,
|
||||||
|
outputLines = [],
|
||||||
|
outputStartFrame,
|
||||||
|
outputStagger = 3,
|
||||||
|
width = 800,
|
||||||
|
height = 450,
|
||||||
|
style,
|
||||||
|
enterFrame = 0,
|
||||||
|
}) => {
|
||||||
|
const frame = useCurrentFrame();
|
||||||
|
const { fps } = useVideoConfig();
|
||||||
|
|
||||||
|
const enterProgress = spring({
|
||||||
|
frame: frame - enterFrame,
|
||||||
|
fps,
|
||||||
|
config: SPRING.snappy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const translateY = interpolate(enterProgress, [0, 1], [height + 50, 0]);
|
||||||
|
const opacity = interpolate(enterProgress, [0, 1], [0, 1]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
transform: `translateY(${translateY}px)`,
|
||||||
|
opacity,
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppWindow title="Terminal" width={width} height={height}>
|
||||||
|
<div style={{ padding: "16px 20px" }}>
|
||||||
|
<div style={{ display: "flex", alignItems: "center" }}>
|
||||||
|
<span style={{ color: "#8b949e", fontSize: 16, fontFamily: "monospace" }}>$ </span>
|
||||||
|
<TypeWriter
|
||||||
|
segments={command}
|
||||||
|
startFrame={commandStartFrame}
|
||||||
|
speed={commandSpeed}
|
||||||
|
style={{ fontSize: 16 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{outputLines.map((line, i) => {
|
||||||
|
const lineFrame = outputStartFrame + i * outputStagger;
|
||||||
|
const lineOpacity = interpolate(frame, [lineFrame, lineFrame + 6], [0, 1], {
|
||||||
|
extrapolateLeft: "clamp",
|
||||||
|
extrapolateRight: "clamp",
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`${line.text}-${i}`}
|
||||||
|
style={{
|
||||||
|
marginTop: i === 0 ? 12 : 4,
|
||||||
|
fontSize: 16,
|
||||||
|
fontFamily: "monospace",
|
||||||
|
color: line.color,
|
||||||
|
opacity: lineOpacity,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{line.text}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</AppWindow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user