Skip to main content
Glama
template.tsx.j26.3 kB
{/* chuk-motion/src/chuk_motion/components/overlays/SubscribeButton/template.tsx.j2 */} import React from 'react'; import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion'; interface SubscribeButtonProps { variant: string; animation: string; position: string; customText: string; startFrame: number; durationInFrames: number; } export const SubscribeButton: React.FC<SubscribeButtonProps> = ({ variant, animation, position, customText, 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; } // Base animation progress 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 ]] } }); // Animation-specific transforms let transform = 'none'; let opacity = interpolate(progress, [0, 1], [0, 1]); if (animation === 'bounce') { const bounceScale = spring({ frame: relativeFrame, fps, config: { damping: 8, mass: 0.5, stiffness: 100 } }); const scale = interpolate(bounceScale, [0, 1], [0.3, 1]); transform = `scale(${scale})`; } else if (animation === 'pulse') { const pulse = Math.sin((relativeFrame / 20) * Math.PI * 2) * 0.1 + 1; transform = `scale(${pulse})`; } else if (animation === 'slide') { const slideDistance = position.includes('right') ? 200 : position.includes('left') ? -200 : 0; const translateX = interpolate(progress, [0, 1], [slideDistance, 0]); transform = `translateX(${translateX}px)`; } else if (animation === 'glow') { const pulse = Math.sin((relativeFrame / 30) * Math.PI * 2) * 0.5 + 1; transform = `scale(${pulse})`; } else if (animation === 'wiggle') { const wiggle = Math.sin((relativeFrame / 5) * Math.PI * 2) * 5; transform = `rotate(${wiggle}deg) scale(${interpolate(progress, [0, 1], [0.5, 1])})`; } // Fade out near end const fadeOut = interpolate( relativeFrame, [durationInFrames - 15, durationInFrames], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); const finalOpacity = opacity * fadeOut; // Position styles const positions: Record<string, any> = { bottom_right: { bottom: parseInt('[[ spacing.spacing['5xl'] ]]'), right: parseInt('[[ spacing.spacing['5xl'] ]]') }, bottom_center: { bottom: parseInt('[[ spacing.spacing['5xl'] ]]'), left: '50%', transform: `${transform} translateX(-50%)` }, center: { top: '50%', left: '50%', transform: `${transform} translate(-50%, -50%)` }, top_right: { top: parseInt('[[ spacing.spacing['5xl'] ]]'), right: parseInt('[[ spacing.spacing['5xl'] ]]') } }; const positionStyle = positions[position] || positions.bottom_right; // Variant styles const variants: Record<string, any> = { minimal: { background: '[[ colors.background.dark ]]', padding: `${parseInt('[[ spacing.spacing.md ]]')}px ${parseInt('[[ spacing.spacing['2xl'] ]]')}px`, borderRadius: parseInt('[[ spacing.border_radius.md ]]'), border: `${parseInt('[[ spacing.border_width.medium ]]')}px solid [[ colors.primary[0] ]]` }, standard: { background: `linear-gradient(135deg, [[ colors.primary[0] ]], [[ colors.accent[0] ]])`, padding: `${parseInt('[[ spacing.spacing.lg ]]')}px ${parseInt('[[ spacing.spacing['2xl'] ]]')}px`, borderRadius: parseInt('[[ spacing.border_radius.lg ]]'), boxShadow: `0 ${parseInt('[[ spacing.spacing.xs ]]')}px ${parseInt('[[ spacing.spacing.xl ]]')}px [[ colors.primary[0] ]]60` }, animated: { background: `linear-gradient(135deg, [[ colors.primary[0] ]], [[ colors.accent[0] ]])`, padding: `${parseInt('[[ spacing.spacing.lg ]]')}px ${parseInt('[[ spacing.spacing['3xl'] ]]')}px`, borderRadius: parseInt('[[ spacing.border_radius.lg ]]'), boxShadow: `0 10px ${parseInt('[[ spacing.spacing['2xl'] ]]')}px [[ colors.primary[0] ]]80`, border: `${parseInt('[[ spacing.border_width.medium ]]')}px solid [[ colors.border.medium ]]` }, '3d': { background: `linear-gradient(135deg, [[ colors.primary[0] ]], [[ colors.accent[0] ]])`, padding: `${parseInt('[[ spacing.spacing.lg ]]')}px ${parseInt('[[ spacing.spacing['3xl'] ]]')}px`, borderRadius: parseInt('[[ spacing.border_radius.lg ]]'), boxShadow: ` 0 10px ${parseInt('[[ spacing.spacing['2xl'] ]]')}px [[ colors.primary[0] ]]80, 0 ${parseInt('[[ spacing.spacing.xxs ]]')}px 0 [[ colors.primary[1] ]], 0 ${parseInt('[[ spacing.spacing.xs ]]')}px ${parseInt('[[ spacing.spacing.lg ]]')}px [[ colors.shadow.dark ]] `, transform: `${transform} translateY(-${parseInt('[[ spacing.spacing.xxs ]]')}px)` } }; const variantStyle = variants[variant] || variants.standard; // Glow effect for certain animations const glowIntensity = animation === 'glow' ? Math.sin((relativeFrame / 30) * Math.PI * 2) * 0.5 + 0.5 : 0; const glowSize = parseInt('[[ spacing.spacing['2xl'] ]]'); return ( <AbsoluteFill style={{ pointerEvents: 'none' }}> <div style={{ position: 'absolute', ...positionStyle, ...variantStyle, opacity: finalOpacity, fontFamily: "'[[ "', '".join(typography.primary_font.fonts) ]]'", fontSize: parseInt('[[ typography.font_sizes[typography.default_resolution].base ]]'), fontWeight: parseInt('[[ typography.font_weights.bold ]]'), color: '[[ colors.text.on_dark ]]', textTransform: 'uppercase', letterSpacing: '0.05em', cursor: 'pointer', userSelect: 'none', ...(animation === 'glow' && { boxShadow: ` ${variantStyle.boxShadow}, 0 0 ${glowSize + glowIntensity * glowSize}px [[ colors.accent[0] ]] ` }) }} > {customText} </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