Skip to main content

YouTube End Card

social-endcard.tsx
import {fontFamily, loadFont} from '@remotion/google-fonts/Inter'; import React from 'react'; import { AbsoluteFill, Easing, Img, Interactive, interpolate, spring, useCurrentFrame, useVideoConfig, } from 'remotion'; loadFont('normal', { weights: ['500'], }); const LinkedInIcon: React.FC<{height: number}> = ({height}) => { return ( <Interactive.Svg name="LinkedIn icon" height={height} viewBox="0 0 448 512" style={{translate: '0 -4px'}} > <path fill="currentColor" d="M100.28 448H7.4V148.9h92.88zM53.79 108.1C24.09 108.1 0 83.5 0 53.8a53.79 53.79 0 0 1 107.58 0c0 29.7-24.1 54.3-53.79 54.3zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.28 61.9 111.28 142.3V448z" /> </Interactive.Svg> ); }; const XIcon: React.FC<{height: number}> = ({height}) => { return ( <Interactive.Svg name="X icon" height={height} viewBox="0 0 512 512"> <path fill="currentColor" d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z" /> </Interactive.Svg> ); }; const LinkIcon: React.FC<{height: number}> = ({height}) => { return ( <Interactive.Svg name="Website icon" height={height} viewBox="0 0 640 512"> <path fill="currentColor" d="M580.2 267.3c56.2-56.2 56.2-147.4 0-203.6S432.8 7.4 376.6 63.7L365.3 75l45.3 45.3 11.3-11.3c31.2-31.2 81.9-31.2 113.1 0s31.2 81.9 0 113.1L421.8 335.2c-31.2 31.2-81.9 31.2-113.1 0c-25.6-25.6-30.3-64.3-13.8-94.6c1.8-3.4 3.9-6.7 6.3-9.8l-51.2-38.4c-4.3 5.7-8.1 11.6-11.4 17.8c-29.5 54.6-21.3 124.2 24.9 170.3c56.2 56.2 147.4 56.2 203.6 0L580.2 267.3zM59.8 244.7c-56.2 56.2-56.2 147.4 0 203.6s147.4 56.2 203.6 0L274.7 437l-45.3-45.3-11.3 11.3c-31.2 31.2-81.9 31.2-113.1 0s-31.2-81.9 0-113.1L218.2 176.8c31.2-31.2 81.9-31.2 113.1 0c25.6 25.6 30.3 64.3 13.8 94.6c-1.8 3.4-3.9 6.7-6.3 9.8l51.2 38.4c4.3-5.7 8.1-11.6 11.4-17.8c29.5-54.6 21.3-124.2-24.9-170.3c-56.2-56.2-147.4-56.2-203.6 0L59.8 244.7z" /> </Interactive.Svg> ); }; const InstagramIcon: React.FC<{height: number}> = ({height}) => { return ( <Interactive.Svg name="Instagram icon" height={height} viewBox="0 0 448 512" > <path fill="currentColor" d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7 2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z" /> </Interactive.Svg> ); }; const SocialLink: React.FC<{ children: React.ReactNode; icon: React.ReactNode; indexFromLast: number; }> = ({children, icon, indexFromLast}) => { const frame = useCurrentFrame(); return ( <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', paddingTop: 20, paddingBottom: 20, opacity: interpolate( frame, [ 35 + ((indexFromLast - 1) / 4) * (30 - 15), 50 + ((indexFromLast - 1) / 4) * (30 - 15), ], [0, 1], { easing: Easing.spring({damping: 200}), extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }, ), }} > <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: 60, width: 140, translate: '0 4px', }} > {icon} </div> <div style={{width: 30}} /> {children} </div> ); }; const WebsiteRow = () => { const {fps} = useVideoConfig(); const frame = useCurrentFrame(); const opacity = spring({ fps, frame, config: { damping: 200, }, delay: 35, durationInFrames: 15, }); return ( <> <div style={{height: 80}} /> <Interactive.Div name="Website row" style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', paddingTop: 20, paddingBottom: 20, opacity, }} > <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: 60, width: 140, translate: '0 4px', }} > <LinkIcon height={60} /> </div> <div style={{width: 30}} /> <Interactive.Div name="Website" style={{ fontSize: 50, fontFamily, fontWeight: 500, marginLeft: 20, }} > remotion.dev </Interactive.Div> </Interactive.Div> </> ); }; const SubscribeButton = () => { return ( <Interactive.Div style={{ height: 140, borderRadius: 70, width: 400, backgroundColor: 'black', color: 'white', display: 'flex', justifyContent: 'center', alignItems: 'center', fontSize: 50, fontFamily, fontWeight: 500, }} name="Subscribe button" > Subscribe </Interactive.Div> ); }; const Avatar = () => { return ( <Img style={{ height: 140, width: 140, borderRadius: '50%', boxShadow: '0px 0px 20px rgba(0, 0, 0, 0.2)', }} src="https://remotion.media/elements/social-endcard-remotion-logo.png" /> ); }; const SubscribeCTA = () => { return ( <div style={{display: 'inline-flex', alignItems: 'center'}}> <Avatar /> <div style={{width: 30}} /> <SubscribeButton /> </div> ); }; const LeftSide = () => { const frame = useCurrentFrame(); return ( <Interactive.Div style={{ display: 'flex', position: 'absolute', height: '100%', flexDirection: 'column', left: 80, justifyContent: 'center', }} showInTimeline={false} > <Interactive.Div name={'Call to action'} style={{ display: 'inline-block', translate: interpolate(frame, [35, 65], ['0px 281px', '0px 0px'], { easing: Easing.spring({damping: 200}), extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }), }} > <SubscribeCTA /> </Interactive.Div> <div style={{marginTop: 80}}> <Interactive.Div name="Instagram row"> <SocialLink icon={<InstagramIcon height={70} />} indexFromLast={4}> <Interactive.Div name="Instagram handle" style={{ fontSize: 50, fontFamily, fontWeight: 500, marginLeft: 20, }} > @remotion </Interactive.Div> </SocialLink> </Interactive.Div> <Interactive.Div name="LinkedIn row"> <SocialLink icon={<LinkedInIcon height={60} />} indexFromLast={3}> <Interactive.Div name="LinkedIn handle" style={{ fontSize: 50, fontFamily, fontWeight: 500, marginLeft: 20, }} > Remotion </Interactive.Div> </SocialLink> </Interactive.Div> <Interactive.Div name="X row"> <SocialLink icon={<XIcon height={60} />} indexFromLast={2}> <Interactive.Div name="X handle" style={{ fontSize: 50, fontFamily, fontWeight: 500, marginLeft: 20, }} > @remotion </Interactive.Div> </SocialLink> </Interactive.Div> <WebsiteRow /> </div> </Interactive.Div> ); }; export const YouTubeEndCard = () => { return ( <AbsoluteFill style={{backgroundColor: '#FAFAFA'}} name="Container"> <LeftSide /> <Interactive.Div name="Top thumbnail" style={{ position: 'absolute', top: 135, right: 100, width: 631, height: 361, border: '6px solid black', }} /> <Interactive.Div name="Bottom thumbnail" style={{ position: 'absolute', right: 100, bottom: 155, width: 631, height: 361, border: '6px solid black', }} /> </AbsoluteFill> ); };