|
| 1 | +import { useEffect } from 'react' |
| 2 | +import { animate, motion, useMotionValue, useTransform } from 'framer-motion' |
| 3 | + |
| 4 | +import { Tooltip } from '@Common/Tooltip' |
| 5 | + |
| 6 | +import { CX, CY, END_ANGLE, RADIUS, START_ANGLE } from './constants' |
| 7 | +import { AnimatedTimerProps } from './types' |
| 8 | + |
| 9 | +const polarToCartesian = (cx: number, cy: number, r: number, angleInDegrees: number) => { |
| 10 | + const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0 |
| 11 | + return { |
| 12 | + x: cx + r * Math.cos(angleInRadians), |
| 13 | + y: cy + r * Math.sin(angleInRadians), |
| 14 | + } |
| 15 | +} |
| 16 | + |
| 17 | +const describeArcPath = (cx: number, cy: number, r: number, endAngle: number) => { |
| 18 | + const startAngle = 0 |
| 19 | + const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1' |
| 20 | + const start = polarToCartesian(cx, cy, r, endAngle) |
| 21 | + const end = polarToCartesian(cx, cy, r, startAngle) |
| 22 | + |
| 23 | + return ` |
| 24 | + M ${cx} ${cy} |
| 25 | + L ${start.x} ${start.y} |
| 26 | + A ${r} ${r} 0 ${largeArcFlag} 0 ${end.x} ${end.y} |
| 27 | + Z |
| 28 | + ` |
| 29 | +} |
| 30 | + |
| 31 | +const AnimatedTimer = ({ duration, onComplete, size = 24, tooltipContent }: AnimatedTimerProps) => { |
| 32 | + const angle = useMotionValue<number>(START_ANGLE) |
| 33 | + const d = useTransform(angle, (currentAngle) => describeArcPath(CX, CY, RADIUS, currentAngle)) |
| 34 | + useEffect(() => { |
| 35 | + const controls = animate(angle, END_ANGLE, { |
| 36 | + duration, |
| 37 | + ease: 'easeInOut', |
| 38 | + onComplete, |
| 39 | + }) |
| 40 | + return controls.stop |
| 41 | + }, [duration]) |
| 42 | + |
| 43 | + return ( |
| 44 | + <Tooltip alwaysShowTippyOnHover={!!tooltipContent} content={tooltipContent}> |
| 45 | + <div className="flex"> |
| 46 | + <svg |
| 47 | + width={CX * 2} |
| 48 | + height={CY * 2} |
| 49 | + viewBox={`0 0 ${CX * 2} ${CY * 2}`} |
| 50 | + fill="none" |
| 51 | + xmlns="http://www.w3.org/2000/svg" |
| 52 | + className={`icon-dim-${size}`} |
| 53 | + > |
| 54 | + <path |
| 55 | + fillRule="evenodd" |
| 56 | + clipRule="evenodd" |
| 57 | + d="M12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3ZM1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 1 18.0751 1 12Z" |
| 58 | + fill="var(--O500)" |
| 59 | + /> |
| 60 | + |
| 61 | + <motion.path fill="var(--O200)" d={d} /> |
| 62 | + </svg> |
| 63 | + </div> |
| 64 | + </Tooltip> |
| 65 | + ) |
| 66 | +} |
| 67 | + |
| 68 | +export default AnimatedTimer |
0 commit comments