From ba7186fe7b9f9e85819f795591386606e2159814 Mon Sep 17 00:00:00 2001 From: Satyam Rastogi Date: Sat, 30 May 2026 02:30:14 +0530 Subject: [PATCH] feat(film): screen frame (with placeholder fallback) + ken burns --- .../product-film/src/components/KenBurns.tsx | 15 ++++++ .../src/components/ScreenFrame.tsx | 48 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 marketing/product-film/src/components/KenBurns.tsx create mode 100644 marketing/product-film/src/components/ScreenFrame.tsx diff --git a/marketing/product-film/src/components/KenBurns.tsx b/marketing/product-film/src/components/KenBurns.tsx new file mode 100644 index 0000000..dfce3f5 --- /dev/null +++ b/marketing/product-film/src/components/KenBurns.tsx @@ -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
{children}
; +}; diff --git a/marketing/product-film/src/components/ScreenFrame.tsx b/marketing/product-film/src/components/ScreenFrame.tsx new file mode 100644 index 0000000..5f4b4ac --- /dev/null +++ b/marketing/product-film/src/components/ScreenFrame.tsx @@ -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 ( + +
+
+ + + + app.lynkeduppro.com +
+
+ {errored ? ( +
+ [capture missing: {captureId}] +
+ ) : ( + setErrored(true)} + style={{ width: "100%", height: "100%", objectFit: "cover", objectPosition: "top center", display: "block" }} + /> + )} +
+
+
+ ); +};