import React from 'react';
import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion';
interface LowerThirdProps {
name: string;
title?: string;
startFrame: number;
durationInFrames: number;
}
export const LowerThird: React.FC<LowerThirdProps> = ({
name,
title,
startFrame,
durationInFrames
}) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const relativeFrame = frame - startFrame;
// Don't render if outside the time range
if (frame < startFrame || frame >= startFrame + durationInFrames) {
return null;
}
// Animation: slide in from left
const slideIn = spring({
frame: relativeFrame,
fps,
config: {
damping: 200,
mass: 0.5,
stiffness: 200
}
});
// Fade in
const opacity = interpolate(relativeFrame, [0, 10], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
});
// Fade out near end
const fadeOut = interpolate(
relativeFrame,
[durationInFrames - 10, durationInFrames],
[1, 0],
{
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
}
);
const finalOpacity = Math.min(opacity, fadeOut);
// Position based on config
const positions = {
bottom_left: { bottom: 40, left: interpolate(slideIn, [0, 1], [-400, 40]) },
bottom_center: { bottom: 40, left: '50%', transform: `translateX(-50%) translateX(${interpolate(slideIn, [0, 1], [-400, 0])}px)` },
bottom_right: { bottom: 40, right: interpolate(slideIn, [0, 1], [-400, 40]) },
top_left: { top: 40, left: interpolate(slideIn, [0, 1], [-400, 40]) },
top_center: { top: 40, left: '50%', transform: `translateX(-50%) translateX(${interpolate(slideIn, [0, 1], [-400, 0])}px)` }
};
const position = positions['bottom_left'] || positions.bottom_left;
// Variant styles
const variants = {
minimal: {
background: 'rgba(0, 0, 0, 0.6)',
backdropFilter: 'none',
padding: '16px 24px',
borderRadius: 8
},
standard: {
background: '#0A0E1A',
backdropFilter: 'none',
padding: '20px 32px',
borderRadius: 12,
borderLeft: `4px solid #0066FF`
},
glass: {
background: 'rgba(10, 14, 26, 0.85)',
backdropFilter: 'blur(10px)',
padding: '20px 32px',
borderRadius: 12,
border: '1px solid rgba(255, 255, 255, 0.1)'
},
bold: {
background: `linear-gradient(135deg, #0066FF, #00D9FF)`,
backdropFilter: 'none',
padding: '24px 40px',
borderRadius: 12,
boxShadow: '0 10px 40px rgba(0, 0, 0, 0.3)'
},
animated: {
background: 'rgba(10, 14, 26, 0.85)',
backdropFilter: 'blur(10px)',
padding: '20px 32px',
borderRadius: 12,
border: `2px solid #00D9FF`,
boxShadow: `0 0 20px #00D9FF80`
}
};
const variantStyle = variants['glass'] || variants.glass;
return (
<AbsoluteFill style={{ pointerEvents: 'none' }}>
<div
style={{
position: 'absolute',
...position,
...variantStyle,
opacity: finalOpacity,
fontFamily: "'Inter', sans-serif"
}}
>
<div
style={{
fontSize: 32,
fontWeight: 700,
color: '#FFFFFF',
marginBottom: title ? 4 : 0,
lineHeight: 1.2
}}
>
{name}
</div>
{title && (
<div
style={{
fontSize: 20,
fontWeight: 500,
color: '#00D9FF',
lineHeight: 1.3
}}
>
{title}
</div>
)}
</div>
</AbsoluteFill>
);
};