WEBLEB
Home
AI Editor
Login
Pro
English
English
Français
Español
Português
Deutsch
Italiano
हिंदी
Fireworks with mouse
3360
Andev.web
Open In Editor
Video
Publish Your Code
11
Recommended
14 June 2024
social media icon with hover
16 December 2025
Navigation Bar with Drop down & Styling Template
19 December 2025
HTML Login Form Template with Social Login
HTML
Copy
Andev Web
CSS
Copy
*{ box-sizing: border-box; } html, body{ height: 100%; } body{ overflow: hidden; display: grid; color: white; background: black; }
JS
Copy
class PointerParticle { constructor(spread, speed, component) { const { ctx, pointer, hue } = component; this.ctx = ctx; this.x = pointer.x; this.y = pointer.y; this.mx = pointer.mx * 0.1; this.my = pointer.my * 0.1; this.size = Math.random() + 1; this.decay = 0.01; this.speed = speed * 0.08; this.spread = spread * this.speed; this.spreadX = (Math.random() - 0.5) * this.spread - this.mx; this.spreadY = (Math.random() - 0.5) * this.spread - this.my; this.color = `hsl(${hue}, 90%, 60%)`; } draw() { this.ctx.fillStyle = this.color; this.ctx.beginPath(); this.ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); this.ctx.fill(); } collapse() { this.size -= this.decay; } trail() { this.x += this.spreadX * this.size; this.y += this.spreadY * this.size; } update() { this.draw(); this.trail(); this.collapse(); } } class PointerParticles extends HTMLElement { static register(tag = "pointer-particles") { if ("customElements" in window) { customElements.define(tag, this); } } static css = ` :host { display: grid; width: 100%; height: 100%; user-select: none; } `; constructor() { super(); this.canvas; this.ctx; this.fps = 60; this.msPerFrame = 1000 / this.fps; this.timePrevious; this.particles = []; this.pointer = { x: 0, y: 0, mx: 0, my: 0 }; this.hue = 0; } connectedCallback() { const canvas = document.createElement("canvas"); const sheet = new CSSStyleSheet(); this.shadowroot = this.attachShadow({ mode: "open" }); sheet.replaceSync(PointerParticles.css); this.shadowroot.adoptedStyleSheets = [sheet]; this.shadowroot.append(canvas); this.canvas = this.shadowroot.querySelector("canvas"); this.ctx = this.canvas.getContext("2d"); this.setCanvasDimensions(); this.setupEvents(); this.timePrevious = performance.now(); this.animateParticles(); } createParticles(event, { count, speed, spread }) { this.setPointerValues(event); for (let i = 0; i < count; i++) { this.particles.push(new PointerParticle(spread, speed, this)); } } setPointerValues(event) { this.pointer.x = event.x - this.offsetLeft; this.pointer.y = event.y - this.offsetTop; this.pointer.mx = event.movementX; this.pointer.my = event.movementY; } setupEvents() { const parent = this.parentNode; parent.addEventListener("click", (event) => { this.createParticles(event, { count: 300, speed: Math.random() + 1, spread: Math.random() + 50 }); }); parent.addEventListener("pointermove", (event) => { this.createParticles(event, { count: 20, speed: this.getSpeed(event), spread: 1 }); }); window.addEventListener("resize", () => this.setCanvasDimensions()); } getSpeed(event) { const a = event.movementX; const b = event.movementY; const c = Math.floor(Math.sqrt(a * a + b * b)); return c; } handleParticles() { for (let i = 0; i < this.particles.length; i++) { this.particles[i].update(); if (this.particles[i].size <= 0.1) { this.particles.splice(i, 1); i--; } } } setCanvasDimensions() { const rect = this.parentNode.getBoundingClientRect(); this.canvas.width = rect.width; this.canvas.height = rect.height; } animateParticles() { requestAnimationFrame(() => this.animateParticles()); const timeNow = performance.now(); const timePassed = timeNow - this.timePrevious; if (timePassed < this.msPerFrame) return; const excessTime = timePassed % this.msPerFrame; this.timePrevious = timeNow - excessTime; this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); this.hue = this.hue > 360 ? 0 : (this.hue += 3); this.handleParticles(); } } PointerParticles.register();