UNPKG

rn-countdown-timer

Version:

Lightweight React Native countdown timer component with color and progress animation based on SVG.

2 lines (1 loc) 3.55 kB
import w from"react";import{View as V}from"react-native";import N,{Path as D}from"react-native-svg";import{useEffect as O,useRef as U,useState as j}from"react";import{useState as L,useRef as C,useCallback as q,useLayoutEffect as W}from"react";var P=({isPlaying:r,duration:e,startAt:t=0,updateInterval:o=0,onComplete:m,onUpdate:n})=>{let[a,c]=L(t),u=C(0),p=C(t),f=C(t*-1e3),i=C(null),s=C(null),d=C(null),S=h=>{let l=h/1e3;if(s.current===null){s.current=l,i.current=requestAnimationFrame(S);return}let T=l-s.current,b=u.current+T;s.current=l,u.current=b;let g=p.current+(o===0?b:(b/o|0)*o),y=p.current+b,x=typeof e=="number"&&y>=e;c(x?e:g),x||(i.current=requestAnimationFrame(S))},R=()=>{i.current&&cancelAnimationFrame(i.current),d.current&&clearTimeout(d.current),s.current=null},k=q(h=>{R(),u.current=0;let l=typeof h=="number"?h:t;p.current=l,c(l),r&&(i.current=requestAnimationFrame(S))},[r,t]);return W(()=>{if(n?.(a),e&&a>=e){f.current+=e*1e3;let{shouldRepeat:h=!1,delay:l=0,newStartAt:T}=m?.(f.current/1e3)||{};h&&(d.current=setTimeout(()=>k(T),l*1e3))}},[a,e]),W(()=>(r&&(i.current=requestAnimationFrame(S)),R),[r,e,o]),{elapsedTime:a,reset:k}};var A=(r,e,t)=>{let o=r/2,m=e/2,n=o-m,a=2*n,c=t==="clockwise"?"1,0":"0,1",u=2*Math.PI*n;return{path:`m ${o},${m} a ${n},${n} 0 ${c} 0,${a} a ${n},${n} 0 ${c} 0,-${a}`,pathLength:u}},B=(r,e)=>r===0||r===e?0:typeof e=="number"?r-e:0,G=r=>({position:"relative",width:r,height:r}),E={display:"flex",justifyContent:"center",alignItems:"center",position:"absolute",left:0,top:0,width:"100%",height:"100%"};var v=(r,e,t,o,m)=>{if(o===0)return e;let n=(m?o-r:r)/o;return e+t*n},I=r=>r.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,(e,t,o,m)=>`#${t}${t}${o}${o}${m}${m}`).substring(1).match(/.{2}/g)?.map(e=>parseInt(e,16))??[],z=(r,e)=>{let{colors:t,colorsTime:o,isSmoothColorTransition:m=!0}=r;if(typeof t=="string")return t;let n=o?.findIndex((i,s)=>i>=e&&e>=o[s+1])??-1;if(!o||n===-1)return t[0];if(!m)return t[n];let a=o[n]-e,c=o[n]-o[n+1],u=I(t[n]),p=I(t[n+1]),f=!!r.isGrowing;return`rgb(${u.map((i,s)=>v(a,i,p[s]-i,c,f)|0).join(",")})`},$=r=>{let{key:e,duration:t,initialRemainingTime:o,updateInterval:m,size:n=180,strokeWidth:a=12,trailStrokeWidth:c,isPlaying:u=!1,isGrowing:p=!1,rotation:f="clockwise",onComplete:i,onUpdate:s}=r,d=U(),[S,R]=j(0),k=Math.max(a,c??0),{path:h,pathLength:l}=A(n,k,f);O(()=>{R(0)},[e]);let{elapsedTime:T}=P({isPlaying:u,duration:t,startAt:B(t,o),updateInterval:m,onUpdate:typeof s=="function"?g=>{let y=Math.ceil(t-g);y!==d.current&&(d.current=y,s(y))}:void 0,onComplete:typeof i=="function"?g=>{let{shouldRepeat:y,delay:x,newInitialRemainingTime:F}=i(g)??{};if(y)return{shouldRepeat:y,delay:x,newStartAt:B(t,F)}}:void 0}),b=t-T;return{elapsedTime:T,path:h,pathLength:l,remainingTime:Math.ceil(b),rotation:f,size:n,stroke:z(r,b),strokeDashoffset:v(T,0,l,t,p),strokeWidth:a}};var M=r=>{let{children:e,duration:t,strokeLinecap:o,trailColor:m,trailStrokeWidth:n}=r,{path:a,pathLength:c,stroke:u,strokeDashoffset:p,remainingTime:f,elapsedTime:i,size:s,strokeWidth:d}=$(r);return w.createElement(V,{style:G(s)},w.createElement(N,{viewBox:`0 0 ${s} ${s}`,width:s,height:s},w.createElement(D,{d:a,fill:"none",stroke:m??"#d9d9d9",strokeWidth:n??d}),i!==t&&w.createElement(D,{d:a,fill:"none",stroke:u,strokeLinecap:o??"round",strokeWidth:d,strokeDasharray:c,strokeDashoffset:p})),typeof e=="function"&&w.createElement(V,{style:E},e({remainingTime:f,elapsedTime:i,color:u})))};M.displayName="CountdownCircleTimer";export{M as CountdownCircleTimer,$ as useCountdown,P as useElapsedTime};