Untitleddesign7_ayntq_250

THANK YOU FOR CONNECTING WITH US

We're thrilled to have the opportunity to collaborate with forward-thinking brands like yours. Whether you're looking to boost your online presence, refine your digital strategy, or scale your marketing efforts, our team is here to guide you every step of the way.

At Astra Flow Media, we believe in creating smart, data-driven solutions that not only capture attention but also drive real results. We'll be in touch shortly to learn more about your goals and explore how we can help your brand flow with purpose in the ever-evolving digital landscape.

'use client'; import React, { useRef, useEffect, useCallback, useMemo } from "react"; import { gsap } from "gsap"; import { InertiaPlugin } from "gsap/InertiaPlugin"; import "./DotGrid.css"; gsap.registerPlugin(InertiaPlugin); const throttle = (func: (...args: any[]) => void, limit: number) => { let lastCall = 0; return function (this: any, ...args: any[]) { const now = performance.now(); if (now - lastCall >= limit) { lastCall = now; func.apply(this, args); } }; }; interface Dot { cx: number; cy: number; xOffset: number; yOffset: number; _inertiaApplied: boolean; } export interface DotGridProps { dotSize?: number; gap?: number; baseColor?: string; activeColor?: string; proximity?: number; speedTrigger?: number; shockRadius?: number; shockStrength?: number; maxSpeed?: number; resistance?: number; returnDuration?: number; className?: string; style?: React.CSSProperties; } function hexToRgb(hex: string) { const m = hex.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i); if (!m) return { r: 0, g: 0, b: 0 }; return { r: parseInt(m[1], 16), g: parseInt(m[2], 16), b: parseInt(m[3], 16), }; } const DotGrid: React.FC = ({ dotSize = 16, gap = 32, baseColor ="#5227FF", activeColor ="#5227FF", proximity = 150, speedTrigger = 100, shockRadius = 250, shockStrength = 5, maxSpeed = 5000, resistance = 750, returnDuration = 1.5, className ="", style, }) => { const wrapperRef = useRef(null); const canvasRef = useRef(null); const dotsRef = useRef([]); const pointerRef = useRef({ x: 0, y: 0, vx: 0, vy: 0, speed: 0, lastTime: 0, lastX: 0, lastY: 0, }); const baseRgb = useMemo(() => hexToRgb(baseColor), [baseColor]); const activeRgb = useMemo(() => hexToRgb(activeColor), [activeColor]); const circlePath = useMemo(() => { if (typeof window ==="undefined" || !window.Path2D) return null; const p = new Path2D(); p.arc(0, 0, dotSize / 2, 0, Math.PI * 2); return p; }, [dotSize]); const buildGrid = useCallback(() => { const wrap = wrapperRef.current; const canvas = canvasRef.current; if (!wrap || !canvas) return; const { width, height } = wrap.getBoundingClientRect(); const dpr = window.devicePixelRatio || 1; canvas.width = width * dpr; canvas.height = height * dpr; canvas.style.width = `${width}px`; canvas.style.height = `${height}px`; const ctx = canvas.getContext("2d"); if (ctx) ctx.scale(dpr, dpr); const cols = Math.floor((width + gap) / (dotSize + gap)); const rows = Math.floor((height + gap) / (dotSize + gap)); const cell = dotSize + gap; const gridW = cell * cols - gap; const gridH = cell * rows - gap; const extraX = width - gridW; const extraY = height - gridH; const startX = extraX / 2 + dotSize / 2; const startY = extraY / 2 + dotSize / 2; const dots: Dot[] = []; for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { const cx = startX + x * cell; const cy = startY + y * cell; dots.push({ cx, cy, xOffset: 0, yOffset: 0, _inertiaApplied: false }); } } dotsRef.current = dots; }, [dotSize, gap]); useEffect(() => { if (!circlePath) return; let rafId: number; const proxSq = proximity * proximity; const draw = () => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; ctx.clearRect(0, 0, canvas.width, canvas.height); const { x: px, y: py } = pointerRef.current; for (const dot of dotsRef.current) { const ox = dot.cx + dot.xOffset; const oy = dot.cy + dot.yOffset; const dx = dot.cx - px; const dy = dot.cy - py; const dsq = dx * dx + dy * dy; let style = baseColor; if (dsq <= proxSq) { const dist = Math.sqrt(dsq); const t = 1 - dist / proximity; const r = Math.round(baseRgb.r + (activeRgb.r - baseRgb.r) * t); const g = Math.round(baseRgb.g + (activeRgb.g - baseRgb.g) * t); const b = Math.round(baseRgb.b + (activeRgb.b - baseRgb.b) * t); style = `rgb(${r},${g},${b})`; } ctx.save(); ctx.translate(ox, oy); ctx.fillStyle = style; ctx.fill(circlePath); ctx.restore(); } rafId = requestAnimationFrame(draw); }; draw(); return () => cancelAnimationFrame(rafId); }, [proximity, baseColor, activeRgb, baseRgb, circlePath]); useEffect(() => { buildGrid(); let ro: ResizeObserver | null = null; if ("ResizeObserver" in window) { ro = new ResizeObserver(buildGrid); wrapperRef.current && ro.observe(wrapperRef.current); } else { (window as Window).addEventListener("resize", buildGrid); } return () => { if (ro) ro.disconnect(); else window.removeEventListener("resize", buildGrid); }; }, [buildGrid]); useEffect(() => { const onMove = (e: MouseEvent) => { const now = performance.now(); const pr = pointerRef.current; const dt = pr.lastTime ? now - pr.lastTime : 16; const dx = e.clientX - pr.lastX; const dy = e.clientY - pr.lastY; let vx = (dx / dt) * 1000; let vy = (dy / dt) * 1000; let speed = Math.hypot(vx, vy); if (speed > maxSpeed) { const scale = maxSpeed / speed; vx *= scale; vy *= scale; speed = maxSpeed; } pr.lastTime = now; pr.lastX = e.clientX; pr.lastY = e.clientY; pr.vx = vx; pr.vy = vy; pr.speed = speed; const rect = canvasRef.current!.getBoundingClientRect(); pr.x = e.clientX - rect.left; pr.y = e.clientY - rect.top; for (const dot of dotsRef.current) { const dist = Math.hypot(dot.cx - pr.x, dot.cy - pr.y); if (speed > speedTrigger && dist < proximity && !dot._inertiaApplied) { dot._inertiaApplied = true; gsap.killTweensOf(dot); const pushX = dot.cx - pr.x + vx * 0.005; const pushY = dot.cy - pr.y + vy * 0.005; gsap.to(dot, { inertia: { xOffset: pushX, yOffset: pushY, resistance }, onComplete: () => { gsap.to(dot, { xOffset: 0, yOffset: 0, duration: returnDuration, ease: "elastic.out(1,0.75)", }); dot._inertiaApplied = false; }, }); } } }; const onClick = (e: MouseEvent) => { const rect = canvasRef.current!.getBoundingClientRect(); const cx = e.clientX - rect.left; const cy = e.clientY - rect.top; for (const dot of dotsRef.current) { const dist = Math.hypot(dot.cx - cx, dot.cy - cy); if (dist < shockRadius && !dot._inertiaApplied) { dot._inertiaApplied = true; gsap.killTweensOf(dot); const falloff = Math.max(0, 1 - dist / shockRadius); const pushX = (dot.cx - cx) * shockStrength * falloff; const pushY = (dot.cy - cy) * shockStrength * falloff; gsap.to(dot, { inertia: { xOffset: pushX, yOffset: pushY, resistance }, onComplete: () => { gsap.to(dot, { xOffset: 0, yOffset: 0, duration: returnDuration, ease: "elastic.out(1,0.75)", }); dot._inertiaApplied = false; }, }); } } }; const throttledMove = throttle(onMove, 50); window.addEventListener("mousemove", throttledMove, { passive: true }); window.addEventListener("click", onClick); return () => { window.removeEventListener("mousemove", throttledMove); window.removeEventListener("click", onClick); }; }, [ maxSpeed, speedTrigger, proximity, resistance, returnDuration, shockRadius, shockStrength, ]); return (
); }; export default DotGrid;
qxnza_138_L7nUwom2PY138logo

THANK YOU FOR CONNECTING WITH US

       We're thrilled to have the opportunity to collaborate with forward-thinking brands like yours. Whether you're looking to boost your online presence, refine your digital strategy, or scale your marketing efforts, our team is here to guide you every step of the way.

       At Astra Flow Media, we believe in creating smart, data-driven solutions that not only capture attention but also drive real results. We'll be in touch shortly to learn more about your goals and explore how we can help your brand flow with purpose in the ever-evolving digital landscape.

+Add Element

We can also Help With all your
Digital Marketing Needs

From latest Ai agents deployment to traditional social media handling. From designing LOGO to creating advertisments, everthying on us.

Digital Media &
PPC Advertising

g5mjk_238_FuLCAZm5h2238image1

Content
Marketing Service

k2mjq_237_mDx6L7OKOA237image2

Search Engine Optimization

ewmdu_260_2g51iQNhDn260image3

Web Design & It’s Marketing

c5mzg_228_p9P3RRklFm228image4
Untitleddesign7_ayntq_250

All Rights Reserved | Design by AstraFlowMedia