test
This commit is contained in:
		
							parent
							
								
									01c469fbbb
								
							
						
					
					
						commit
						474750fbb9
					
				| @ -1,18 +1,11 @@ | |||||||
| "use client"; | "use client"; | ||||||
| 
 | 
 | ||||||
| import { cn } from "@/lib/utils"; | import { cn } from "@/lib/utils"; | ||||||
| import React, { useEffect, useRef, useState } from "react"; | import React, { useEffect, useRef, useState, useCallback } from "react"; | ||||||
| 
 | 
 | ||||||
| interface MousePosition { | // Custom Hook für Mausposition
 | ||||||
|   x: number; | function useMousePosition() { | ||||||
|   y: number; |   const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function MousePosition(): MousePosition { |  | ||||||
|   const [mousePosition, setMousePosition] = useState<MousePosition>({ |  | ||||||
|     x: 0, |  | ||||||
|     y: 0, |  | ||||||
|   }); |  | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     const handleMouseMove = (event: MouseEvent) => { |     const handleMouseMove = (event: MouseEvent) => { | ||||||
| @ -20,15 +13,39 @@ function MousePosition(): MousePosition { | |||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     window.addEventListener("mousemove", handleMouseMove); |     window.addEventListener("mousemove", handleMouseMove); | ||||||
| 
 |     return () => window.removeEventListener("mousemove", handleMouseMove); | ||||||
|     return () => { |  | ||||||
|       window.removeEventListener("mousemove", handleMouseMove); |  | ||||||
|     }; |  | ||||||
|   }, []); |   }, []); | ||||||
| 
 | 
 | ||||||
|   return mousePosition; |   return mousePosition; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Umwandlung von HEX in RGB
 | ||||||
|  | function hexToRgb(hex: string): number[] { | ||||||
|  |   hex = hex.replace("#", ""); | ||||||
|  |   if (hex.length === 3) { | ||||||
|  |     hex = hex.split("").map((char) => char + char).join(""); | ||||||
|  |   } | ||||||
|  |   return [ | ||||||
|  |     parseInt(hex.substring(0, 2), 16), | ||||||
|  |     parseInt(hex.substring(2, 4), 16), | ||||||
|  |     parseInt(hex.substring(4, 6), 16), | ||||||
|  |   ]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Partikel-Interface
 | ||||||
|  | interface Particle { | ||||||
|  |   x: number; | ||||||
|  |   y: number; | ||||||
|  |   translateX: number; | ||||||
|  |   translateY: number; | ||||||
|  |   size: number; | ||||||
|  |   alpha: number; | ||||||
|  |   targetAlpha: number; | ||||||
|  |   dx: number; | ||||||
|  |   dy: number; | ||||||
|  |   magnetism: number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| interface ParticlesProps { | interface ParticlesProps { | ||||||
|   className?: string; |   className?: string; | ||||||
|   quantity?: number; |   quantity?: number; | ||||||
| @ -40,22 +57,6 @@ interface ParticlesProps { | |||||||
|   vx?: number; |   vx?: number; | ||||||
|   vy?: number; |   vy?: number; | ||||||
| } | } | ||||||
| function hexToRgb(hex: string): number[] { |  | ||||||
|   hex = hex.replace("#", ""); |  | ||||||
| 
 |  | ||||||
|   if (hex.length === 3) { |  | ||||||
|     hex = hex |  | ||||||
|       .split("") |  | ||||||
|       .map((char) => char + char) |  | ||||||
|       .join(""); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   const hexInt = parseInt(hex, 16); |  | ||||||
|   const red = (hexInt >> 16) & 255; |  | ||||||
|   const green = (hexInt >> 8) & 255; |  | ||||||
|   const blue = hexInt & 255; |  | ||||||
|   return [red, green, blue]; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| const Particles: React.FC<ParticlesProps> = ({ | const Particles: React.FC<ParticlesProps> = ({ | ||||||
|   className = "", |   className = "", | ||||||
| @ -71,210 +72,112 @@ const Particles: React.FC<ParticlesProps> = ({ | |||||||
|   const canvasRef = useRef<HTMLCanvasElement>(null); |   const canvasRef = useRef<HTMLCanvasElement>(null); | ||||||
|   const canvasContainerRef = useRef<HTMLDivElement>(null); |   const canvasContainerRef = useRef<HTMLDivElement>(null); | ||||||
|   const context = useRef<CanvasRenderingContext2D | null>(null); |   const context = useRef<CanvasRenderingContext2D | null>(null); | ||||||
|   const circles = useRef<Circle[]>([]); |   const circles = useRef<Particle[]>([]); | ||||||
|   const mousePosition = MousePosition(); |   const animationFrameRef = useRef<number | null>(null); | ||||||
|   const mouse = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); |   const mousePosition = useMousePosition(); | ||||||
|   const canvasSize = useRef<{ w: number; h: number }>({ w: 0, h: 0 }); |   const mouse = useRef({ x: 0, y: 0 }); | ||||||
|  |   const canvasSize = useRef({ w: 0, h: 0 }); | ||||||
|   const dpr = typeof window !== "undefined" ? window.devicePixelRatio : 1; |   const dpr = typeof window !== "undefined" ? window.devicePixelRatio : 1; | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     if (canvasRef.current) { |  | ||||||
|       context.current = canvasRef.current.getContext("2d"); |  | ||||||
|     } |  | ||||||
|     initCanvas(); |  | ||||||
|     animate(); |  | ||||||
|     window.addEventListener("resize", initCanvas); |  | ||||||
| 
 |  | ||||||
|     return () => { |  | ||||||
|       window.removeEventListener("resize", initCanvas); |  | ||||||
|     }; |  | ||||||
|   }, [color]); |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     onMouseMove(); |  | ||||||
|   }, [mousePosition.x, mousePosition.y]); |  | ||||||
| 
 |  | ||||||
|   useEffect(() => { |  | ||||||
|     initCanvas(); |  | ||||||
|   }, [refresh]); |  | ||||||
| 
 |  | ||||||
|   const initCanvas = () => { |  | ||||||
|     resizeCanvas(); |  | ||||||
|     drawParticles(); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   const onMouseMove = () => { |  | ||||||
|     if (canvasRef.current) { |  | ||||||
|       const rect = canvasRef.current.getBoundingClientRect(); |  | ||||||
|       const { w, h } = canvasSize.current; |  | ||||||
|       const x = mousePosition.x - rect.left - w / 2; |  | ||||||
|       const y = mousePosition.y - rect.top - h / 2; |  | ||||||
|       const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2; |  | ||||||
|       if (inside) { |  | ||||||
|         mouse.current.x = x; |  | ||||||
|         mouse.current.y = y; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   type Circle = { |  | ||||||
|     x: number; |  | ||||||
|     y: number; |  | ||||||
|     translateX: number; |  | ||||||
|     translateY: number; |  | ||||||
|     size: number; |  | ||||||
|     alpha: number; |  | ||||||
|     targetAlpha: number; |  | ||||||
|     dx: number; |  | ||||||
|     dy: number; |  | ||||||
|     magnetism: number; |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   const resizeCanvas = () => { |  | ||||||
|     if (canvasContainerRef.current && canvasRef.current && context.current) { |  | ||||||
|       circles.current.length = 0; |  | ||||||
|       canvasSize.current.w = canvasContainerRef.current.offsetWidth; |  | ||||||
|       canvasSize.current.h = canvasContainerRef.current.offsetHeight; |  | ||||||
|       canvasRef.current.width = canvasSize.current.w * dpr; |  | ||||||
|       canvasRef.current.height = canvasSize.current.h * dpr; |  | ||||||
|       canvasRef.current.style.width = `${canvasSize.current.w}px`; |  | ||||||
|       canvasRef.current.style.height = `${canvasSize.current.h}px`; |  | ||||||
|       context.current.scale(dpr, dpr); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   const circleParams = (): Circle => { |  | ||||||
|     const x = Math.floor(Math.random() * canvasSize.current.w); |  | ||||||
|     const y = Math.floor(Math.random() * canvasSize.current.h); |  | ||||||
|     const translateX = 0; |  | ||||||
|     const translateY = 0; |  | ||||||
|     const pSize = Math.floor(Math.random() * 2) + size; |  | ||||||
|     const alpha = 0; |  | ||||||
|     const targetAlpha = parseFloat((Math.random() * 0.6 + 0.1).toFixed(1)); |  | ||||||
|     const dx = (Math.random() - 0.5) * 0.1; |  | ||||||
|     const dy = (Math.random() - 0.5) * 0.1; |  | ||||||
|     const magnetism = 0.1 + Math.random() * 4; |  | ||||||
|     return { |  | ||||||
|       x, |  | ||||||
|       y, |  | ||||||
|       translateX, |  | ||||||
|       translateY, |  | ||||||
|       size: pSize, |  | ||||||
|       alpha, |  | ||||||
|       targetAlpha, |  | ||||||
|       dx, |  | ||||||
|       dy, |  | ||||||
|       magnetism, |  | ||||||
|     }; |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   const rgb = hexToRgb(color); |   const rgb = hexToRgb(color); | ||||||
| 
 | 
 | ||||||
|   const drawCircle = (circle: Circle, update = false) => { |   const resizeCanvas = useCallback(() => { | ||||||
|  |     if (!canvasContainerRef.current || !canvasRef.current) return; | ||||||
|  |     const { offsetWidth: w, offsetHeight: h } = canvasContainerRef.current; | ||||||
|  |     Object.assign(canvasSize.current, { w, h }); | ||||||
|  | 
 | ||||||
|  |     canvasRef.current.width = w * dpr; | ||||||
|  |     canvasRef.current.height = h * dpr; | ||||||
|  |     canvasRef.current.style.width = `${w}px`; | ||||||
|  |     canvasRef.current.style.height = `${h}px`; | ||||||
|  | 
 | ||||||
|     if (context.current) { |     if (context.current) { | ||||||
|       const { x, y, translateX, translateY, size, alpha } = circle; |       context.current.scale(dpr, dpr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     circles.current = Array.from({ length: quantity }, createParticle); | ||||||
|  |   }, [quantity]); | ||||||
|  | 
 | ||||||
|  |   const createParticle = (): Particle => ({ | ||||||
|  |     x: Math.random() * canvasSize.current.w, | ||||||
|  |     y: Math.random() * canvasSize.current.h, | ||||||
|  |     translateX: 0, | ||||||
|  |     translateY: 0, | ||||||
|  |     size: Math.random() * 2 + size, | ||||||
|  |     alpha: 0, | ||||||
|  |     targetAlpha: Math.random() * 0.6 + 0.1, | ||||||
|  |     dx: (Math.random() - 0.5) * 0.1, | ||||||
|  |     dy: (Math.random() - 0.5) * 0.1, | ||||||
|  |     magnetism: 0.1 + Math.random() * 4, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   const clearCanvas = () => { | ||||||
|  |     context.current?.clearRect(0, 0, canvasSize.current.w, canvasSize.current.h); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const drawParticle = (particle: Particle) => { | ||||||
|  |     if (!context.current) return; | ||||||
|  |     const { x, y, translateX, translateY, size, alpha } = particle; | ||||||
|  |     context.current.save(); | ||||||
|     context.current.translate(translateX, translateY); |     context.current.translate(translateX, translateY); | ||||||
|     context.current.beginPath(); |     context.current.beginPath(); | ||||||
|     context.current.arc(x, y, size, 0, 2 * Math.PI); |     context.current.arc(x, y, size, 0, 2 * Math.PI); | ||||||
|     context.current.fillStyle = `rgba(${rgb.join(",")}, ${alpha})`; |     context.current.fillStyle = `rgba(${rgb.join(",")}, ${alpha})`; | ||||||
|     context.current.fill(); |     context.current.fill(); | ||||||
|       context.current.setTransform(dpr, 0, 0, dpr, 0, 0); |     context.current.restore(); | ||||||
| 
 |  | ||||||
|       if (!update) { |  | ||||||
|         circles.current.push(circle); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   const clearContext = () => { |   const animateParticles = () => { | ||||||
|     if (context.current) { |     clearCanvas(); | ||||||
|       context.current.clearRect( |     circles.current.forEach((particle) => { | ||||||
|         0, |       particle.x += particle.dx + vx; | ||||||
|         0, |       particle.y += particle.dy + vy; | ||||||
|         canvasSize.current.w, |       particle.translateX += (mouse.current.x / (staticity / particle.magnetism) - particle.translateX) / ease; | ||||||
|         canvasSize.current.h, |       particle.translateY += (mouse.current.y / (staticity / particle.magnetism) - particle.translateY) / ease; | ||||||
|       ); |       particle.alpha = Math.min(particle.alpha + 0.02, particle.targetAlpha); | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| 
 | 
 | ||||||
|   const drawParticles = () => { |       drawParticle(particle); | ||||||
|     clearContext(); |  | ||||||
|     const particleCount = quantity; |  | ||||||
|     for (let i = 0; i < particleCount; i++) { |  | ||||||
|       const circle = circleParams(); |  | ||||||
|       drawCircle(circle); |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
| 
 | 
 | ||||||
|   const remapValue = ( |  | ||||||
|     value: number, |  | ||||||
|     start1: number, |  | ||||||
|     end1: number, |  | ||||||
|     start2: number, |  | ||||||
|     end2: number, |  | ||||||
|   ): number => { |  | ||||||
|     const remapped = |  | ||||||
|       ((value - start1) * (end2 - start2)) / (end1 - start1) + start2; |  | ||||||
|     return remapped > 0 ? remapped : 0; |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   const animate = () => { |  | ||||||
|     clearContext(); |  | ||||||
|     circles.current.forEach((circle: Circle, i: number) => { |  | ||||||
|       // Handle the alpha value
 |  | ||||||
|       const edge = [ |  | ||||||
|         circle.x + circle.translateX - circle.size, // distance from left edge
 |  | ||||||
|         canvasSize.current.w - circle.x - circle.translateX - circle.size, // distance from right edge
 |  | ||||||
|         circle.y + circle.translateY - circle.size, // distance from top edge
 |  | ||||||
|         canvasSize.current.h - circle.y - circle.translateY - circle.size, // distance from bottom edge
 |  | ||||||
|       ]; |  | ||||||
|       const closestEdge = edge.reduce((a, b) => Math.min(a, b)); |  | ||||||
|       const remapClosestEdge = parseFloat( |  | ||||||
|         remapValue(closestEdge, 0, 20, 0, 1).toFixed(2), |  | ||||||
|       ); |  | ||||||
|       if (remapClosestEdge > 1) { |  | ||||||
|         circle.alpha += 0.02; |  | ||||||
|         if (circle.alpha > circle.targetAlpha) { |  | ||||||
|           circle.alpha = circle.targetAlpha; |  | ||||||
|         } |  | ||||||
|       } else { |  | ||||||
|         circle.alpha = circle.targetAlpha * remapClosestEdge; |  | ||||||
|       } |  | ||||||
|       circle.x += circle.dx + vx; |  | ||||||
|       circle.y += circle.dy + vy; |  | ||||||
|       circle.translateX += |  | ||||||
|         (mouse.current.x / (staticity / circle.magnetism) - circle.translateX) / |  | ||||||
|         ease; |  | ||||||
|       circle.translateY += |  | ||||||
|         (mouse.current.y / (staticity / circle.magnetism) - circle.translateY) / |  | ||||||
|         ease; |  | ||||||
| 
 |  | ||||||
|       drawCircle(circle, true); |  | ||||||
| 
 |  | ||||||
|       // circle gets out of the canvas
 |  | ||||||
|       if ( |       if ( | ||||||
|         circle.x < -circle.size || |         particle.x < -particle.size || | ||||||
|         circle.x > canvasSize.current.w + circle.size || |         particle.x > canvasSize.current.w + particle.size || | ||||||
|         circle.y < -circle.size || |         particle.y < -particle.size || | ||||||
|         circle.y > canvasSize.current.h + circle.size |         particle.y > canvasSize.current.h + particle.size | ||||||
|       ) { |       ) { | ||||||
|         // remove the circle from the array
 |         Object.assign(particle, createParticle()); | ||||||
|         circles.current.splice(i, 1); |  | ||||||
|         // create a new circle
 |  | ||||||
|         const newCircle = circleParams(); |  | ||||||
|         drawCircle(newCircle); |  | ||||||
|         // update the circle position
 |  | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|     window.requestAnimationFrame(animate); |     animationFrameRef.current = requestAnimationFrame(animateParticles); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (canvasRef.current) { | ||||||
|  |       context.current = canvasRef.current.getContext("2d"); | ||||||
|  |       resizeCanvas(); | ||||||
|  |       animateParticles(); | ||||||
|  |       window.addEventListener("resize", resizeCanvas); | ||||||
|  |     } | ||||||
|  |     return () => { | ||||||
|  |       if (animationFrameRef.current) { | ||||||
|  |         cancelAnimationFrame(animationFrameRef.current); | ||||||
|  |       } | ||||||
|  |       window.removeEventListener("resize", resizeCanvas); | ||||||
|  |     }; | ||||||
|  |   }, [resizeCanvas, refresh]); | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (!canvasRef.current) return; | ||||||
|  |     const rect = canvasRef.current.getBoundingClientRect(); | ||||||
|  |     const { w, h } = canvasSize.current; | ||||||
|  |     const x = mousePosition.x - rect.left - w / 2; | ||||||
|  |     const y = mousePosition.y - rect.top - h / 2; | ||||||
|  |     if (x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2) { | ||||||
|  |       mouse.current.x = x; | ||||||
|  |       mouse.current.y = y; | ||||||
|  |     } | ||||||
|  |   }, [mousePosition]); | ||||||
|  | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div |     <div className={cn("pointer-events-none", className)} ref={canvasContainerRef} aria-hidden="true"> | ||||||
|       className={cn("pointer-events-none", className)} |  | ||||||
|       ref={canvasContainerRef} |  | ||||||
|       aria-hidden="true" |  | ||||||
|     > |  | ||||||
|       <canvas ref={canvasRef} className="size-full" /> |       <canvas ref={canvasRef} className="size-full" /> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 CanbiZ
						CanbiZ