import React, { useEffect } from 'react'; import { animated, useSpring } from 'react-spring'; import { useTheme } from 'styled-components'; import { usePrevious } from '../hooks/'; type AnimatedHeartIconProps = { active: boolean; variant?: 'default' | 'light'; scaleBy?: number; color?: string; width?: number; height?: number; onAnimationComplete?: () => void; }; const STEP_DURATION_MS = 200; export const TOTAL_DURATION_MS = STEP_DURATION_MS * 2; const AnimatedHeartIcon = ({ active, variant = 'default', color = '#000000', width = 24, height = 24, scaleBy = 1.4, onAnimationComplete, }: AnimatedHeartIconProps) => { const { colors } = useTheme(); const prevActive = usePrevious(active); useEffect(() => { if (active && !prevActive) { setTimeout(() => { onAnimationComplete?.(); }, TOTAL_DURATION_MS); } }, [active, prevActive]); const fadeInAfterAnimationComplete = useSpring({ opacity: active ? 1 : 0, delay: TOTAL_DURATION_MS, config: { duration: TOTAL_DURATION_MS }, }); const outlineAnimation = useSpring({ fill: active ? colors.primary[variant === 'light' ? 400 : 700] : colors.neutral[600], config: { duration: STEP_DURATION_MS }, }); const growAnimationFirst = useSpring({ transform: active ? 'scale(1)' : 'scale(0)', config: { duration: STEP_DURATION_MS }, }); const growAnimationSecond = useSpring({ transform: active ? 'scale(1)' : 'scale(0)', delay: STEP_DURATION_MS, config: { duration: STEP_DURATION_MS }, }); const { keyframe } = useSpring({ keyframe: active ? 1 : 0, delay: 0, config: { duration: active ? TOTAL_DURATION_MS : 0 }, }); return ( `scale(${x})`), }} > {!active ? ( ) : ( <> {/* First growing heart [lowest z-index] - don't show on the light variant */} {variant == 'default' && ( )} {/* Heart outline */} {/* Second growing heart */} {/* Final filled heart [highest z-index] */} )} ); }; export default AnimatedHeartIcon;