Skip to main content
Glama
template.tsx.j27.8 kB
{/* chuk-motion/src/chuk_motion/components/overlays/TextOverlay/template.tsx.j2 */} import React from 'react'; import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion'; interface TextOverlayProps { text: string; style: string; animation: string; position: string; startFrame: number; durationInFrames: number; } export const TextOverlay: React.FC<TextOverlayProps> = ({ text, style, animation, position, 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 logic let opacity = 1; let transform = 'none'; let filter = 'none'; let displayText = text; if (animation === 'blur_in') { const progress = spring({ frame: relativeFrame, fps, config: { damping: [[ motion.default_spring.config.damping ]], mass: [[ motion.default_spring.config.mass ]], stiffness: [[ motion.default_spring.config.stiffness ]] } }); opacity = interpolate(progress, [0, 1], [0, 1]); const blur = interpolate(progress, [0, 1], [20, 0]); filter = `blur(${blur}px)`; } else if (animation === 'slide_up') { const progress = spring({ frame: relativeFrame, fps, config: { damping: [[ motion.default_spring.config.damping ]], mass: [[ motion.default_spring.config.mass ]], stiffness: [[ motion.default_spring.config.stiffness ]] } }); opacity = interpolate(progress, [0, 1], [0, 1]); const translateY = interpolate(progress, [0, 1], [100, 0]); transform = `translateY(${translateY}px)`; } else if (animation === 'fade') { opacity = interpolate(relativeFrame, [0, 15], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }); } else if (animation === 'typewriter') { const charsToShow = Math.floor(interpolate(relativeFrame, [0, 60], [0, text.length], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' })); displayText = text.slice(0, charsToShow); opacity = 1; } else if (animation === 'scale_in') { const progress = spring({ frame: relativeFrame, fps, config: { damping: [[ motion.default_spring.config.damping ]], mass: [[ motion.default_spring.config.mass ]], stiffness: [[ motion.default_spring.config.stiffness ]] } }); opacity = interpolate(progress, [0, 1], [0, 1]); const scale = interpolate(progress, [0, 1], [0.5, 1]); transform = `scale(${scale})`; } // Fade out at the end const fadeOut = interpolate( relativeFrame, [durationInFrames - 15, durationInFrames], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); const finalOpacity = opacity * fadeOut; // Style variants const styles: Record<string, any> = { emphasis: { fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution]['3xl'] ]]'), fontWeight: parseInt('[[ typography.font_weights.black ]]'), color: '[[ colors.text.on_dark ]]', textAlign: 'center', textShadow: `0 ${parseInt('[[ spacing.spacing.xxs ]]')}px ${parseInt('[[ spacing.spacing.lg ]]')}px [[ colors.primary[0] ]]80`, letterSpacing: '-0.02em', lineHeight: 1.1, maxWidth: 1200, background: `linear-gradient(135deg, [[ colors.primary[0] ]], [[ colors.accent[0] ]])`, WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text' }, caption: { fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].lg ]]'), fontWeight: parseInt('[[ typography.font_weights.semibold ]]'), color: '[[ colors.text.on_dark ]]', textAlign: 'center', background: '[[ colors.background.glass ]]', backdropFilter: 'blur(10px)', padding: `[[ spacing.spacing.lg ]] [[ spacing.spacing['2xl'] ]]`, borderRadius: parseInt('[[ spacing.border_radius.lg ]]'), maxWidth: 1400, lineHeight: parseFloat('[[ typography.line_heights.normal ]]') }, callout: { fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution]['2xl'] ]]'), fontWeight: parseInt('[[ typography.font_weights.extrabold ]]'), color: '[[ colors.text.on_dark ]]', textAlign: 'center', background: `linear-gradient(135deg, [[ colors.primary[0] ]], [[ colors.accent[0] ]])`, padding: `[[ spacing.spacing.xl ]] [[ spacing.spacing['3xl'] ]]`, borderRadius: parseInt('[[ spacing.spacing.md ]]'), boxShadow: `0 ${parseInt('[[ spacing.spacing.xs ]]')}px ${parseInt('[[ spacing.spacing['2xl'] ]]')}px [[ colors.primary[0] ]]60`, textTransform: 'uppercase', letterSpacing: '0.05em', maxWidth: 1200, lineHeight: 1.2 }, subtitle: { fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].xl ]]'), fontWeight: parseInt('[[ typography.font_weights.semibold ]]'), color: '[[ colors.text.on_dark ]]', textAlign: 'center', background: '[[ colors.background.dark ]]', padding: `[[ spacing.spacing.lg ]] [[ spacing.spacing['2xl'] ]]`, borderRadius: parseInt('[[ spacing.border_radius.md ]]'), maxWidth: 1600, lineHeight: parseFloat('[[ typography.line_heights.normal ]]'), border: '2px solid [[ colors.border.light ]]' }, quote: { fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].xl ]]'), fontWeight: parseInt('[[ typography.font_weights.semibold ]]'), color: '[[ colors.text.on_dark ]]', textAlign: 'center', fontStyle: 'italic', maxWidth: 1200, lineHeight: parseFloat('[[ typography.line_heights.snug ]]'), borderLeft: `${parseInt('[[ spacing.spacing.xs ]]')}px solid [[ colors.accent[0] ]]`, paddingLeft: parseInt('[[ spacing.spacing['2xl'] ]]'), position: 'relative' } }; const styleConfig = styles[style] || styles.emphasis; // Position configuration const positions: Record<string, any> = { center: { justifyContent: 'center', alignItems: 'center' }, top: { justifyContent: 'flex-start', alignItems: 'center', paddingTop: parseInt('[[ spacing.spacing['4xl'] ]]') }, bottom: { justifyContent: 'flex-end', alignItems: 'center', paddingBottom: parseInt('[[ spacing.spacing['4xl'] ]]') }, top_left: { justifyContent: 'flex-start', alignItems: 'flex-start', padding: parseInt('[[ spacing.spacing['4xl'] ]]') }, top_right: { justifyContent: 'flex-start', alignItems: 'flex-end', padding: parseInt('[[ spacing.spacing['4xl'] ]]') }, bottom_left: { justifyContent: 'flex-end', alignItems: 'flex-start', padding: parseInt('[[ spacing.spacing['4xl'] ]]') }, bottom_right: { justifyContent: 'flex-end', alignItems: 'flex-end', padding: parseInt('[[ spacing.spacing['4xl'] ]]') } }; const positionConfig = positions[position] || positions.center; return ( <AbsoluteFill style={{ display: 'flex', flexDirection: 'column', ...positionConfig, fontFamily: "'[[ "', '".join(typography.body_font.fonts) ]]'", pointerEvents: 'none', padding: parseInt('[[ spacing.spacing['3xl'] ]]') }} > <div style={{ ...styleConfig, opacity: finalOpacity, transform, filter }} > {displayText} </div> </AbsoluteFill> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrishayuk/chuk-mcp-remotion'

If you have feedback or need assistance with the MCP directory API, please join our Discord server