feat(film): screen frame (with placeholder fallback) + ken burns
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
import { useCurrentFrame, useVideoConfig, interpolate } from "remotion";
|
||||
|
||||
// Slow zoom/pan over its children across the whole sequence the component lives in.
|
||||
export const KenBurns: React.FC<{
|
||||
children: React.ReactNode;
|
||||
from?: number;
|
||||
to?: number;
|
||||
durationInFrames: number;
|
||||
}> = ({ children, from = 1.0, to = 1.08, durationInFrames }) => {
|
||||
const frame = useCurrentFrame();
|
||||
const scale = interpolate(frame, [0, durationInFrames], [from, to], {
|
||||
extrapolateRight: "clamp",
|
||||
});
|
||||
return <div style={{ width: "100%", height: "100%", transform: `scale(${scale})` }}>{children}</div>;
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
import { useState } from "react";
|
||||
import { AbsoluteFill, staticFile } from "remotion";
|
||||
import { theme } from "../theme";
|
||||
|
||||
export const ScreenFrame: React.FC<{
|
||||
captureId: string;
|
||||
scale?: number;
|
||||
}> = ({ captureId, scale = 0.86 }) => {
|
||||
const [errored, setErrored] = useState(false);
|
||||
const src = staticFile(`captures/${captureId}.png`);
|
||||
|
||||
return (
|
||||
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center", backgroundColor: theme.bg }}>
|
||||
<div
|
||||
style={{
|
||||
width: `${scale * 100}%`,
|
||||
aspectRatio: "16 / 9",
|
||||
borderRadius: 16,
|
||||
overflow: "hidden",
|
||||
border: `1px solid ${theme.border}`,
|
||||
boxShadow: "0 40px 120px rgba(0,0,0,0.55)",
|
||||
backgroundColor: "#0c0c0e",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<div style={{ height: 34, display: "flex", alignItems: "center", gap: 8, padding: "0 14px", background: "rgba(255,255,255,0.04)", borderBottom: `1px solid ${theme.border}` }}>
|
||||
<span style={{ width: 11, height: 11, borderRadius: 999, background: "#ef4444" }} />
|
||||
<span style={{ width: 11, height: 11, borderRadius: 999, background: "#f59e0b" }} />
|
||||
<span style={{ width: 11, height: 11, borderRadius: 999, background: "#10b981" }} />
|
||||
<span style={{ marginLeft: 14, color: theme.textDim, fontFamily: theme.mono, fontSize: 13 }}>app.lynkeduppro.com</span>
|
||||
</div>
|
||||
<div style={{ position: "absolute", top: 34, left: 0, right: 0, bottom: 0 }}>
|
||||
{errored ? (
|
||||
<div style={{ width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: theme.textDim, fontFamily: theme.mono, fontSize: 22, background: "#0c0c0e" }}>
|
||||
[capture missing: {captureId}]
|
||||
</div>
|
||||
) : (
|
||||
<img
|
||||
src={src}
|
||||
onError={() => setErrored(true)}
|
||||
style={{ width: "100%", height: "100%", objectFit: "cover", objectPosition: "top center", display: "block" }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user