UNPKG

@arolariu/components

Version:

🎨 70+ beautiful, accessible React components built on Base UI. TypeScript-first, CSS Modules styling, tree-shakeable, SSR-ready. Perfect for modern web apps, design systems & rapid prototyping. Zero config, maximum flexibility! ⚡

365 lines (364 loc) • 13 kB
"use client"; import { jsx, jsxs } from "react/jsx-runtime"; import { motion } from "motion/react"; import { cn } from "../../lib/utilities.js"; import hole_background_module from "./hole-background.module.js"; import * as __rspack_external_react from "react"; const linear = (progress)=>progress; const easeInExpo = (progress)=>0 === progress ? 0 : 2 ** (10 * (progress - 1)); const createEmptyDisc = ()=>({ p: 0, x: 0, y: 0, w: 0, h: 0 }); const createEmptyParticleArea = ()=>({ sx: 0, sw: 0, ex: 0, ew: 0, h: 0 }); const createInitialState = ()=>({ discs: [], lines: [], particles: [], clip: { disc: createEmptyDisc(), i: 0, path: null }, startDisc: createEmptyDisc(), endDisc: createEmptyDisc(), rect: { width: 0, height: 0 }, render: { width: 0, height: 0, dpi: 1 }, particleArea: createEmptyParticleArea(), linesCanvas: null }); const HoleBackground = /*#__PURE__*/ __rspack_external_react.forwardRef(({ strokeColor = "#737373", numberOfLines = 50, numberOfDiscs = 50, particleRGBColor = [ 255, 255, 255 ], className, children, ...props }, ref)=>{ const canvasRef = __rspack_external_react.useRef(null); const animationFrameIdRef = __rspack_external_react.useRef(0); const stateRef = __rspack_external_react.useRef(createInitialState()); __rspack_external_react.useImperativeHandle(ref, ()=>canvasRef.current, []); const tweenValue = __rspack_external_react.useCallback((start, end, progress, ease = null)=>{ const delta = end - start; const easeFunction = "inExpo" === ease ? easeInExpo : linear; return start + delta * easeFunction(progress); }, []); const tweenDisc = __rspack_external_react.useCallback((disc)=>{ const { startDisc, endDisc } = stateRef.current; disc.x = tweenValue(startDisc.x, endDisc.x, disc.p); disc.y = tweenValue(startDisc.y, endDisc.y, disc.p, "inExpo"); disc.w = tweenValue(startDisc.w, endDisc.w, disc.p); disc.h = tweenValue(startDisc.h, endDisc.h, disc.p); }, [ tweenValue ]); const setSize = __rspack_external_react.useCallback(()=>{ const canvas = canvasRef.current; if (!canvas) return; const rect = canvas.getBoundingClientRect(); stateRef.current.rect = { width: rect.width, height: rect.height }; stateRef.current.render = { width: rect.width, height: rect.height, dpi: globalThis.window.devicePixelRatio || 1 }; canvas.width = stateRef.current.render.width * stateRef.current.render.dpi; canvas.height = stateRef.current.render.height * stateRef.current.render.dpi; }, []); const setDiscs = __rspack_external_react.useCallback(()=>{ const { width, height } = stateRef.current.rect; stateRef.current.discs = []; stateRef.current.startDisc = { x: 0.5 * width, y: 0.45 * height, w: 0.75 * width, h: 0.7 * height, p: 0 }; stateRef.current.endDisc = { x: 0.5 * width, y: 0.95 * height, w: 0, h: 0, p: 0 }; let previousBottom = height; stateRef.current.clip = { disc: createEmptyDisc(), i: 0, path: null }; for(let index = 0; index < numberOfDiscs; index += 1){ const progress = index / numberOfDiscs; const disc = { p: progress, x: 0, y: 0, w: 0, h: 0 }; tweenDisc(disc); const bottom = disc.y + disc.h; if (bottom <= previousBottom) stateRef.current.clip = { disc: { ...disc }, i: index, path: null }; previousBottom = bottom; stateRef.current.discs.push(disc); } const clipPath = new globalThis.Path2D(); const { disc } = stateRef.current.clip; clipPath.ellipse(disc.x, disc.y, disc.w, disc.h, 0, 0, 2 * Math.PI); clipPath.rect(disc.x - disc.w, 0, 2 * disc.w, disc.y); stateRef.current.clip.path = clipPath; }, [ numberOfDiscs, tweenDisc ]); const setLines = __rspack_external_react.useCallback(()=>{ const { width, height } = stateRef.current.rect; stateRef.current.lines = []; const linesAngle = 2 * Math.PI / numberOfLines; for(let index = 0; index < numberOfLines; index += 1)stateRef.current.lines.push([]); stateRef.current.discs.forEach((disc)=>{ for(let index = 0; index < numberOfLines; index += 1){ const angle = index * linesAngle; const point = { x: disc.x + Math.cos(angle) * disc.w, y: disc.y + Math.sin(angle) * disc.h }; stateRef.current.lines[index].push(point); } }); const offCanvas = globalThis.document.createElement("canvas"); offCanvas.width = width; offCanvas.height = height; const context = offCanvas.getContext("2d"); const clipPath = stateRef.current.clip.path; if (!context || !clipPath) return; stateRef.current.lines.forEach((line)=>{ context.save(); let lineIsIn = false; line.forEach((point, lineIndex)=>{ if (0 === lineIndex) return; const previousPoint = line[lineIndex - 1]; if (!lineIsIn && (context.isPointInPath(clipPath, point.x, point.y) || context.isPointInStroke(clipPath, point.x, point.y))) lineIsIn = true; else if (lineIsIn) context.clip(clipPath); context.beginPath(); context.moveTo(previousPoint.x, previousPoint.y); context.lineTo(point.x, point.y); context.strokeStyle = strokeColor; context.lineWidth = 2; context.stroke(); context.closePath(); }); context.restore(); }); stateRef.current.linesCanvas = offCanvas; }, [ numberOfLines, strokeColor ]); const initParticle = __rspack_external_react.useCallback((start = false)=>{ const { particleArea } = stateRef.current; const sx = particleArea.sx + particleArea.sw * Math.random(); const ex = particleArea.ex + particleArea.ew * Math.random(); const dx = ex - sx; const y = start ? particleArea.h * Math.random() : particleArea.h; const radius = 0.5 + 4 * Math.random(); const vy = 0.5 + Math.random(); return { x: sx, sx, dx, y, vy, p: 0, r: radius, c: `rgba(${particleRGBColor[0]}, ${particleRGBColor[1]}, ${particleRGBColor[2]}, ${Math.random()})` }; }, [ particleRGBColor ]); const setParticles = __rspack_external_react.useCallback(()=>{ const { width, height } = stateRef.current.rect; stateRef.current.particles = []; const { disc } = stateRef.current.clip; stateRef.current.particleArea = { sx: (width - 0.5 * disc.w) / 2, sw: 0.5 * disc.w, ex: (width - 2 * disc.w) / 2, ew: 2 * disc.w, h: 0.85 * height }; for(let index = 0; index < 100; index += 1)stateRef.current.particles.push(initParticle(true)); }, [ initParticle ]); const drawDiscs = __rspack_external_react.useCallback((context)=>{ context.strokeStyle = strokeColor; context.lineWidth = 2; const outerDisc = stateRef.current.startDisc; context.beginPath(); context.ellipse(outerDisc.x, outerDisc.y, outerDisc.w, outerDisc.h, 0, 0, 2 * Math.PI); context.stroke(); context.closePath(); const clipPath = stateRef.current.clip.path; stateRef.current.discs.forEach((disc, index)=>{ if (index % 5 !== 0) return; if (clipPath && disc.w < stateRef.current.clip.disc.w - 5) { context.save(); context.clip(clipPath); } context.beginPath(); context.ellipse(disc.x, disc.y, disc.w, disc.h, 0, 0, 2 * Math.PI); context.stroke(); context.closePath(); if (clipPath && disc.w < stateRef.current.clip.disc.w - 5) context.restore(); }); }, [ strokeColor ]); const drawLines = __rspack_external_react.useCallback((context)=>{ if (stateRef.current.linesCanvas) context.drawImage(stateRef.current.linesCanvas, 0, 0); }, []); const drawParticles = __rspack_external_react.useCallback((context)=>{ const clipPath = stateRef.current.clip.path; if (!clipPath) return; context.save(); context.clip(clipPath); stateRef.current.particles.forEach((particle)=>{ context.fillStyle = particle.c; context.beginPath(); context.rect(particle.x, particle.y, particle.r, particle.r); context.closePath(); context.fill(); }); context.restore(); }, []); const moveDiscs = __rspack_external_react.useCallback(()=>{ stateRef.current.discs.forEach((disc)=>{ disc.p = (disc.p + 0.001) % 1; tweenDisc(disc); }); }, [ tweenDisc ]); const moveParticles = __rspack_external_react.useCallback(()=>{ stateRef.current.particles.forEach((particle, index)=>{ particle.p = 1 - particle.y / Math.max(stateRef.current.particleArea.h, 1); particle.x = particle.sx + particle.dx * particle.p; particle.y -= particle.vy; if (particle.y < 0) stateRef.current.particles[index] = initParticle(); }); }, [ initParticle ]); const tick = __rspack_external_react.useCallback(()=>{ const canvas = canvasRef.current; if (!canvas) return; const context = canvas.getContext("2d"); if (!context) return; context.clearRect(0, 0, canvas.width, canvas.height); context.save(); context.scale(stateRef.current.render.dpi, stateRef.current.render.dpi); moveDiscs(); moveParticles(); drawDiscs(context); drawLines(context); drawParticles(context); context.restore(); animationFrameIdRef.current = globalThis.requestAnimationFrame(tick); }, [ drawDiscs, drawLines, drawParticles, moveDiscs, moveParticles ]); const init = __rspack_external_react.useCallback(()=>{ setSize(); setDiscs(); setLines(); setParticles(); }, [ setDiscs, setLines, setParticles, setSize ]); __rspack_external_react.useEffect(()=>{ const canvas = canvasRef.current; if (!canvas) return; init(); tick(); const handleResize = ()=>{ setSize(); setDiscs(); setLines(); setParticles(); }; globalThis.window.addEventListener("resize", handleResize); return ()=>{ globalThis.window.removeEventListener("resize", handleResize); globalThis.cancelAnimationFrame(animationFrameIdRef.current); }; }, [ init, setDiscs, setLines, setParticles, setSize, tick ]); return /*#__PURE__*/ jsxs("div", { className: cn(hole_background_module.root, className), children: [ children, /*#__PURE__*/ jsx("canvas", { ref: canvasRef, className: hole_background_module.canvas, ...props }), /*#__PURE__*/ jsx(motion.div, { "aria-hidden": "true", className: hole_background_module.glow, animate: { backgroundPosition: "0% 300%" }, transition: { duration: 5, ease: "linear", repeat: 1 / 0 } }), /*#__PURE__*/ jsx("div", { "aria-hidden": "true", className: hole_background_module.scanlines }) ] }); }); HoleBackground.displayName = "HoleBackground"; export { HoleBackground }; //# sourceMappingURL=hole-background.js.map