PageSourceSearch

https://reactbits.dev/assets/ClickSparkDemo-CvvfJfGI.js

js reactbits.dev collected 2026-09-25 14:13:15 UTC 19,204 bytes, 650 lines download raw bytes

1import{$ as C,r as g,j as e,B as x,T as R,F as S}from"./index-CQKK0Imd.js";import{u as w,C as T,T as b,P as y,O as z,a as E,b as F,c as P}from"./useComponentProps-CEvMaZsz.js";import{u as M}from"./useForceRerender-DWXB3a6h.js";import{P as s}from"./PreviewSlider-Ch78y-a-.js";import{P as I}from"./PreviewColorPickerCustom-CxB8ex0e.js";import{C as j}from"./Customize-D9LDBJtX.js";import A from"./ClickSpark-D5xBVByV.js";import"./index-2zoGWYkB.js";import"./backgroundThemeProps-DVgeaD0m.js";import"./ShikiCode-C19L5K1F.js";import"./aiExport-CBeFnU7D.js";import"./IconSelect-D5LzCXat.js";import"./sparkles-Df4Xp1Rb.js";import"./file-code-2-CTN1MbwE.js";import"./useScrubGesture-Db-j4kh3.js";import"./preview-slider-zh8UBlwx.js";const L=`import { useRef, useEffect, useCallback } from 'react';
2
3const ClickSpark = ({
4  sparkColor = '#fff',
5  sparkSize = 10,
6  sparkRadius = 15,
7  sparkCount = 8,
8  duration = 400,
9  easing = 'ease-out',
10  extraScale = 1.0,
11  children
12}) => {
13  const canvasRef = useRef(null);
14  const sparksRef = useRef([]);
15  const startTimeRef = useRef(null);
16
17  useEffect(() => {
18    const canvas = canvasRef.current;
19    if (!canvas) return;
20
21    const parent = canvas.parentElement;
22    if (!parent) return;
23
24    let resizeTimeout;
25
26    const resizeCanvas = () => {
27      const { width, height } = parent.getBoundingClientRect();
28      if (canvas.width !== width || canvas.height !== height) {
29        canvas.width = width;
30        canvas.height = height;
31      }
32    };
33
34    const handleResize = () => {
35      clearTimeout(resizeTimeout);
36      resizeTimeout = setTimeout(resizeCanvas, 100);
37    };
38
39    const ro = new ResizeObserver(handleResize);
40    ro.observe(parent);
41
42    resizeCanvas();
43
44    return () => {
45      ro.disconnect();
46      clearTimeout(resizeTimeout);
47    };
48  }, []);
49
50  const easeFunc = useCallback(
51    t => {
52      switch (easing) {
53        case 'linear':
54          return t;
55        case 'ease-in':
56          return t * t;
57        case 'ease-in-out':
58          return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
59        default:
60          return t * (2 - t);
61      }
62    },
63    [easing]
64  );
65
66  useEffect(() => {
67    const canvas = canvasRef.current;
68    if (!canvas) return;
69    const ctx = canvas.getContext('2d');
70
71    let animationId;
72
73    const draw = timestamp => {
74      if (!startTimeRef.current) {
75        startTimeRef.current = timestamp;
76      }
77      ctx.clearRect(0, 0, canvas.width, canvas.height);
78
79      sparksRef.current = sparksRef.current.filter(spark => {
80        const elapsed = timestamp - spark.startTime;
81        if (elapsed >= duration) {
82          return false;
83        }
84
85        const progress = elapsed / duration;
86        const eased = easeFunc(progress);
87
88        const distance = eased * sparkRadius * extraScale;
89        const lineLength = sparkSize * (1 - eased);
90
91        const x1 = spark.x + distance * Math.cos(spark.angle);
92        const y1 = spark.y + distance * Math.sin(spark.angle);
93        const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle);
94        const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle);
95
96        ctx.strokeStyle = sparkColor;
97        ctx.lineWidth = 2;
98        ctx.beginPath();
99        ctx.moveTo(x1, y1);
100        ctx.lineTo(x2, y2);
101        ctx.stroke();
102
103        return true;
104      });
105
106      animationId = requestAnimationFrame(draw);
107    };
108
109    animationId = requestAnimationFrame(draw);
110
111    return () => {
112      cancelAnimationFrame(animationId);
113    };
114  }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]);
115
116  const handleClick = e => {
117    const canvas = canvasRef.current;
118    if (!canvas) return;
119    const rect = canvas.getBoundingClientRect();
120    const x = e.clientX - rect.left;
121    const y = e.clientY - rect.top;
122
123    const now = performance.now();
124    const newSparks = Array.from({ length: sparkCount }, (_, i) => ({
125      x,
126      y,
127      angle: (2 * Math.PI * i) / sparkCount,
128      startTime: now
129    }));
130
131    sparksRef.current.push(...newSparks);
132  };
133
134  return (
135    <div
136      style={{
137        position: 'relative',
138        width: '100%',
139        height: '100%'
140      }}
141      onClick={handleClick}
142    >
143      <canvas
144        ref={canvasRef}
145        style={{
146          width: '100%',
147          height: '100%',
148          display: 'block',
149          userSelect: 'none',
150          position: 'absolute',
151          top: 0,
152          left: 0,
153          pointerEvents: 'none'
154        }}
155      />
156      {children}
157    </div>
158  );
159};
160
161export default ClickSpark;
162`,B=`import { useRef, useEffect, useCallback } from 'react';
163
164const ClickSpark = ({
165  sparkColor = '#fff',
166  sparkSize = 10,
167  sparkRadius = 15,
168  sparkCount = 8,
169  duration = 400,
170  easing = 'ease-out',
171  extraScale = 1.0,
172  children
173}) => {
174  const canvasRef = useRef(null);
175  const sparksRef = useRef([]);
176  const startTimeRef = useRef(null);
177
178  useEffect(() => {
179    const canvas = canvasRef.current;
180    if (!canvas) return;
181
182    const parent = canvas.parentElement;
183    if (!parent) return;
184
185    let resizeTimeout;
186
187    const resizeCanvas = () => {
188      const { width, height } = parent.getBoundingClientRect();
189      if (canvas.width !== width || canvas.height !== height) {
190        canvas.width = width;
191        canvas.height = height;
192      }
193    };
194
195    const handleResize = () => {
196      clearTimeout(resizeTimeout);
197      resizeTimeout = setTimeout(resizeCanvas, 100);
198    };
199
200    const ro = new ResizeObserver(handleResize);
201    ro.observe(parent);
202
203    resizeCanvas();
204
205    return () => {
206      ro.disconnect();
207      clearTimeout(resizeTimeout);
208    };
209  }, []);
210
211  const easeFunc = useCallback(
212    t => {
213      switch (easing) {
214        case 'linear':
215          return t;
216        case 'ease-in':
217          return t * t;
218        case 'ease-in-out':
219          return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
220        default:
221          return t * (2 - t);
222      }
223    },
224    [easing]
225  );
226
227  useEffect(() => {
228    const canvas = canvasRef.current;
229    if (!canvas) return;
230    const ctx = canvas.getContext('2d');
231
232    let animationId;
233
234    const draw = timestamp => {
235      if (!startTimeRef.current) {
236        startTimeRef.current = timestamp;
237      }
238      ctx.clearRect(0, 0, canvas.width, canvas.height);
239
240      sparksRef.current = sparksRef.current.filter(spark => {
241        const elapsed = timestamp - spark.startTime;
242        if (elapsed >= duration) {
243          return false;
244        }
245
246        const progress = elapsed / duration;
247        const eased = easeFunc(progress);
248
249        const distance = eased * sparkRadius * extraScale;
250        const lineLength = sparkSize * (1 - eased);
251
252        const x1 = spark.x + distance * Math.cos(spark.angle);
253        const y1 = spark.y + distance * Math.sin(spark.angle);
254        const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle);
255        const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle);
256
257        ctx.strokeStyle = sparkColor;
258        ctx.lineWidth = 2;
259        ctx.beginPath();
260        ctx.moveTo(x1, y1);
261        ctx.lineTo(x2, y2);
262        ctx.stroke();
263
264        return true;
265      });
266
267      animationId = requestAnimationFrame(draw);
268    };
269
270    animationId = requestAnimationFrame(draw);
271
272    return () => {
273      cancelAnimationFrame(animationId);
274    };
275  }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]);
276
277  const handleClick = e => {
278    const canvas = canvasRef.current;
279    if (!canvas) return;
280    const rect = canvas.getBoundingClientRect();
281    const x = e.clientX - rect.left;
282    const y = e.clientY - rect.top;
283
284    const now = performance.now();
285    const newSparks = Array.from({ length: sparkCount }, (_, i) => ({
286      x,
287      y,
288      angle: (2 * Math.PI * i) / sparkCount,
289      startTime: now
290    }));
291
292    sparksRef.current.push(...newSparks);
293  };
294
295  return (
296    <div className="relative w-full h-full" onClick={handleClick}>
297      <canvas ref={canvasRef} className="w-full h-full block absolute top-0 left-0 select-none pointer-events-none" />
298      {children}
299    </div>
300  );
301};
302
303export default ClickSpark;
304`,N=`import React, { useRef, useEffect, useCallback } from 'react';
305
306interface ClickSparkProps {
307  sparkColor?: string;
308  sparkSize?: number;
309  sparkRadius?: number;
310  sparkCount?: number;
311  duration?: number;
312  easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
313  extraScale?: number;
314  children?: React.ReactNode;
315}
316
317interface Spark {
318  x: number;
319  y: number;
320  angle: number;
321  startTime: number;
322}
323
324const ClickSpark: React.FC<ClickSparkProps> = ({
325  sparkColor = '#fff',
326  sparkSize = 10,
327  sparkRadius = 15,
328  sparkCount = 8,
329  duration = 400,
330  easing = 'ease-out',
331  extraScale = 1.0,
332  children
333}) => {
334  const canvasRef = useRef<HTMLCanvasElement>(null);
335  const sparksRef = useRef<Spark[]>([]);
336  const startTimeRef = useRef<number | null>(null);
337
338  useEffect(() => {
339    const canvas = canvasRef.current;
340    if (!canvas) return;
341
342    const parent = canvas.parentElement;
343    if (!parent) return;
344
345    let resizeTimeout: ReturnType<typeof setTimeout>;
346
347    const resizeCanvas = () => {
348      const { width, height } = parent.getBoundingClientRect();
349      if (canvas.width !== width || canvas.height !== height) {
350        canvas.width = width;
351        canvas.height = height;
352      }
353    };
354
355    const handleResize = () => {
356      clearTimeout(resizeTimeout);
357      resizeTimeout = setTimeout(resizeCanvas, 100);
358    };
359
360    const ro = new ResizeObserver(handleResize);
361    ro.observe(parent);
362
363    resizeCanvas();
364
365    return () => {
366      ro.disconnect();
367      clearTimeout(resizeTimeout);
368    };
369  }, []);
370
371  const easeFunc = useCallback(
372    (t: number) => {
373      switch (easing) {
374        case 'linear':
375          return t;
376        case 'ease-in':
377          return t * t;
378        case 'ease-in-out':
379          return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
380        default:
381          return t * (2 - t);
382      }
383    },
384    [easing]
385  );
386
387  useEffect(() => {
388    const canvas = canvasRef.current;
389    if (!canvas) return;
390    const ctx = canvas.getContext('2d');
391    if (!ctx) return;
392
393    let animationId: number;
394
395    const draw = (timestamp: number) => {
396      if (!startTimeRef.current) {
397        startTimeRef.current = timestamp;
398      }
399      ctx?.clearRect(0, 0, canvas.width, canvas.height);
400
401      sparksRef.current = sparksRef.current.filter((spark: Spark) => {
402        const elapsed = timestamp - spark.startTime;
403        if (elapsed >= duration) {
404          return false;
405        }
406
407        const progress = elapsed / duration;
408        const eased = easeFunc(progress);
409
410        const distance = eased * sparkRadius * extraScale;
411        const lineLength = sparkSize * (1 - eased);
412
413        const x1 = spark.x + distance * Math.cos(spark.angle);
414        const y1 = spark.y + distance * Math.sin(spark.angle);
415        const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle);
416        const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle);
417
418        ctx.strokeStyle = sparkColor;
419        ctx.lineWidth = 2;
420        ctx.beginPath();
421        ctx.moveTo(x1, y1);
422        ctx.lineTo(x2, y2);
423        ctx.stroke();
424
425        return true;
426      });
427
428      animationId = requestAnimationFrame(draw);
429    };
430
431    animationId = requestAnimationFrame(draw);
432
433    return () => {
434      cancelAnimationFrame(animationId);
435    };
436  }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]);
437
438  const handleClick = (e: React.MouseEvent<HTMLDivElement>): void => {
439    const canvas = canvasRef.current;
440    if (!canvas) return;
441    const rect = canvas.getBoundingClientRect();
442    const x = e.clientX - rect.left;
443    const y = e.clientY - rect.top;
444
445    const now = performance.now();
446    const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({
447      x,
448      y,
449      angle: (2 * Math.PI * i) / sparkCount,
450      startTime: now
451    }));
452
453    sparksRef.current.push(...newSparks);
454  };
455
456  return (
457    <div
458      style={{
459        width: '100%',
460        height: '100%',
461        position: 'relative'
462      }}
463      onClick={handleClick}
464    >
465      <canvas
466        ref={canvasRef}
467        style={{
468          position: 'absolute',
469          inset: 0,
470          pointerEvents: 'none'
471        }}
472      />
473      {children}
474    </div>
475  );
476};
477
478export default ClickSpark;
479`,q=`import React, { useRef, useEffect, useCallback } from 'react';
480
481interface ClickSparkProps {
482  sparkColor?: string;
483  sparkSize?: number;
484  sparkRadius?: number;
485  sparkCount?: number;
486  duration?: number;
487  easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
488  extraScale?: number;
489  children?: React.ReactNode;
490}
491
492interface Spark {
493  x: number;
494  y: number;
495  angle: number;
496  startTime: number;
497}
498
499const ClickSpark: React.FC<ClickSparkProps> = ({
500  sparkColor = '#fff',
501  sparkSize = 10,
502  sparkRadius = 15,
503  sparkCount = 8,
504  duration = 400,
505  easing = 'ease-out',
506  extraScale = 1.0,
507  children
508}) => {
509  const canvasRef = useRef<HTMLCanvasElement>(null);
510  const sparksRef = useRef<Spark[]>([]);
511  const startTimeRef = useRef<number | null>(null);
512
513  useEffect(() => {
514    const canvas = canvasRef.current;
515    if (!canvas) return;
516
517    const parent = canvas.parentElement;
518    if (!parent) return;
519
520    let resizeTimeout: ReturnType<typeof setTimeout>;
521
522    const resizeCanvas = () => {
523      const { width, height } = parent.getBoundingClientRect();
524      if (canvas.width !== width || canvas.height !== height) {
525        canvas.width = width;
526        canvas.height = height;
527      }
528    };
529
530    const handleResize = () => {
531      clearTimeout(resizeTimeout);
532      resizeTimeout = setTimeout(resizeCanvas, 100);
533    };
534
535    const ro = new ResizeObserver(handleResize);
536    ro.observe(parent);
537
538    resizeCanvas();
539
540    return () => {
541      ro.disconnect();
542      clearTimeout(resizeTimeout);
543    };
544  }, []);
545
546  const easeFunc = useCallback(
547    (t: number) => {
548      switch (easing) {
549        case 'linear':
550          return t;
551        case 'ease-in':
552          return t * t;
553        case 'ease-in-out':
554          return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
555        default:
556          return t * (2 - t);
557      }
558    },
559    [easing]
560  );
561
562  useEffect(() => {
563    const canvas = canvasRef.current;
564    if (!canvas) return;
565    const ctx = canvas.getContext('2d');
566    if (!ctx) return;
567
568    let animationId: number;
569
570    const draw = (timestamp: number) => {
571      if (!startTimeRef.current) {
572        startTimeRef.current = timestamp;
573      }
574      ctx?.clearRect(0, 0, canvas.width, canvas.height);
575
576      sparksRef.current = sparksRef.current.filter((spark: Spark) => {
577        const elapsed = timestamp - spark.startTime;
578        if (elapsed >= duration) {
579          return false;
580        }
581
582        const progress = elapsed / duration;
583        const eased = easeFunc(progress);
584
585        const distance = eased * sparkRadius * extraScale;
586        const lineLength = sparkSize * (1 - eased);
587
588        const x1 = spark.x + distance * Math.cos(spark.angle);
589        const y1 = spark.y + distance * Math.sin(spark.angle);
590        const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle);
591        const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle);
592
593        ctx.strokeStyle = sparkColor;
594        ctx.lineWidth = 2;
595        ctx.beginPath();
596        ctx.moveTo(x1, y1);
597        ctx.lineTo(x2, y2);
598        ctx.stroke();
599
600        return true;
601      });
602
603      animationId = requestAnimationFrame(draw);
604    };
605
606    animationId = requestAnimationFrame(draw);
607
608    return () => {
609      cancelAnimationFrame(animationId);
610    };
611  }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]);
612
613  const handleClick = (e: React.MouseEvent<HTMLDivElement>): void => {
614    const canvas = canvasRef.current;
615    if (!canvas) return;
616    const rect = canvas.getBoundingClientRect();
617    const x = e.clientX - rect.left;
618    const y = e.clientY - rect.top;
619
620    const now = performance.now();
621    const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({
622      x,
623      y,
624      angle: (2 * Math.PI * i) / sparkCount,
625      startTime: now
626    }));
627
628    sparksRef.current.push(...newSparks);
629  };
630
631  return (
632    <div className="relative w-full h-full" onClick={handleClick}>
633      <canvas ref={canvasRef} className="absolute inset-0 pointer-events-none" />
634      {children}
635    </div>
636  );
637};
638
639export default ClickSpark;
640`,O={usage:`import ClickSpark from './ClickSpark';
641
642<ClickSpark
643  sparkColor='#fff'
644  sparkSize={10}
645  sparkRadius={15}
646  sparkCount={8}
647  duration={400}
648>
649  {/* Your content here */}
650</ClickSpark>`,code:L,tailwind:B,tsCode:N,tsTailwind:q},i={sparkColor:"#ffffff",sparkSize:10,sparkRadius:15,sparkCount:8,duration:400,extraScale:1},te=()=>{const{props:d,updateProp:t,resetProps:k,hasChanges:f}=w(i),{sparkColor:r,sparkSize:c,sparkRadius:o,sparkCount:u,duration:l,extraScale:p}=d,m=C(r===i.sparkColor?"#18181b":r,r),[h,a]=M(),v=g.useMemo(()=>[{name:"sparkColor",type:"string",default:"'#fff'",description:"Color of each spark line."},{name:"sparkSize",type:"number",default:10,description:"Initial length of each spark line."},{name:"sparkRadius",type:"number",default:15,description:"How far sparks travel from the click center."},{name:"sparkCount",type:"number",default:8,description:"Number of spark lines that appear on each click."},{name:"duration",type:"number",default:400,description:"Animation duration in milliseconds."},{name:"easing",type:"string",default:"'ease-out'",description:"Easing function used for the spark animation."},{name:"extraScale",type:"number",default:1,description:"Additional multiplier for spark distance."},{name:"children",type:"React.ReactNode",default:"",description:"React children to render."}],[]);return e.jsx(T,{props:d,defaultProps:i,resetProps:k,hasChanges:f,children:e.jsxs(b,{children:[e.jsxs(y,{children:[e.jsxs(x,{position:"relative",className:"demo-container",h:400,p:0,overflow:"hidden",children:[e.jsx(A,{sparkColor:m,sparkSize:c,sparkRadius:o,sparkCount:u,duration:l,extraScale:p},h),e.jsx(R,{position:"absolute",fontWeight:600,fontSize:"2rem",textAlign:"center",className:"demo-instruction",userSelect:"none",children:"Click Around!"})]}),e.jsx(S,{justify:"flex-end",mt:2,mb:-2,children:e.jsx(z,{backgroundId:"click-spark",currentProps:{sparkColor:r,sparkSize:c,sparkRadius:o,sparkCount:u,duration:l,extraScale:p},defaultProps:i})}),e.jsxs(j,{children:[e.jsx(I,{title:"Spark Color",color:m,onChange:n=>{t("sparkColor",n),a()}}),e.jsx(s,{title:"Spark Size",min:5,max:60,step:1,value:c,onChange:n=>{t("sparkSize",n),a()}}),e.jsx(s,{title:"Spark Radius",min:10,max:200,step:5,value:o,onChange:n=>{t("sparkRadius",n),a()}}),e.jsx(s,{title:"Spark Count",min:1,max:20,step:1,value:u,onChange:n=>{t("sparkCount",n),a()}}),e.jsx(s,{title:"Duration",min:200,max:2e3,step:100,value:l,valueUnit:"ms",onChange:n=>{t("duration",n),a()}}),e.jsx(s,{title:"Extra Scale",min:.5,max:2,step:.1,value:p,onChange:n=>{t("extraScale",n),a()}})]}),e.jsx(E,{data:v})]}),e.jsx(F,{children:e.jsx(P,{codeObject:O,componentName:"ClickSpark"})})]})})};export{te as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.