ROB'S GARAGE DESIGN STUDIO
CSS3 NEON PARTICLES MOUSE TRAIL
Web Page Code Highlights
• CSS and Javascript for Neon Particles Mouse Trail. No Dependencies.
• FX Effects: Neon Rainbow Color Changing Mouse Trail Particles with Explosive on-Click Particles.
This web page Validates at:
Rainbow Neon Particles & on-Click Explosion Mouse Trail Javascript
Creating a mouse trail effect with Particles primarily involves JavaScript to track mouse movement and dynamically add/remove image elements, with CSS used for styling and animation of these images.
Explanation:
• HTML:
A container div is used to hold the dynamically created trail images.
CSS:
• mouse-trail-container: Fixed positioning ensures the container overlays the viewport and pointer-events: none prevents the trail images from blocking clicks or scrolling.
trail-image: Images are absolutely positioned and initially hidden (opacity: 0, transform: scale(0)). A transition property provides smooth fading and scaling effects.
trail-image.active: This class is added by JavaScript to trigger the fade-in and scale-up animation.
JavaScript:
• An event listener on mousemove triggers creation of a new img element (throttled). The image's position is calculated relative to the overlay container using getBoundingClientRect + clientX/clientY. A small requestAnimationFrame-based throttle reduces DOM churn. Images are removed after their transition completes.
Customization:
• Particle Size: Adjust the random size range inside createParticle() and the explosion generator.
• Color Cycling: Modify the global hue increment rate to speed up or slow down rainbow shifting.
• Trail Density: Change spawnDistance to increase or reduce how many particles follow the mouse.
• Particle Lifetime: Adjust the duration values in the Web Animations API (both trail and explosion) as well as the setTimeout removal delays.
• Explosion Strength: Increase/decrease the spread distance, rotation range, and total particle count inside the click event handler.
document.addEventListener("DOMContentLoaded", () => {
const container = document.querySelector(".mouse-trail-container");
let hue = 0; // initial hue for rainbow cycling
const spawnDistance = 20;
let lastX = 0, lastY = 0;
function createParticle(x, y) {
const particle = document.createElement("div");
particle.className = "particle";
const size = Math.random() * 6 + 4; // 4-10px
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.left = `${x}px`;
particle.style.top = `${y}px`;
// Rainbow hue cycling
hue = (hue + 1) % 360;
particle.style.backgroundColor = `hsl(${hue}, 100%, 60%)`;
container.appendChild(particle);
// Random drift animation
const dx = (Math.random() - 0.5) * 60;
const dy = (Math.random() - 0.5) * 60;
particle.animate([
{ transform: `translate(0,0)`, opacity: 1 },
{ transform: `translate(${dx}px, ${dy}px)`, opacity: 0 }
], {
duration: 800 + Math.random() * 400,
easing: "ease-out"
});
setTimeout(() => particle.remove(), 1200);
}
// Handle global mouse movement
window.addEventListener("mousemove", e => {
const dx = e.clientX - lastX;
const dy = e.clientY - lastY;
const dist = Math.sqrt(dx*dx + dy*dy);
if (dist > spawnDistance) {
createParticle(e.clientX, e.clientY);
lastX = e.clientX;
lastY = e.clientY;
}
});
// Click explosion on mouse position
window.addEventListener("click", e => {
const x = e.clientX;
const y = e.clientY;
const explosionParticles = 40 + Math.floor(Math.random()*20);
for(let i=0; i<explosionParticles; i++){
const particle = document.createElement("div");
particle.className = "particle";
const size = Math.random()*8+4;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.left = `${x}px`;
particle.style.top = `${y}px`;
const pHue = (hue + Math.random()*360) % 360;
particle.style.backgroundColor = `hsl(${pHue}, 100%, 70%)`;
container.appendChild(particle);
const angle = Math.random()*2*Math.PI;
const distance = 50 + Math.random()*100;
const dx = Math.cos(angle)*distance;
const dy = Math.sin(angle)*distance;
const rotate = (Math.random()-0.5)*720;
const scale = 0.3 + Math.random();
particle.animate([
{ transform: `translate(0,0) rotate(0deg) scale(1)`, opacity: 1 },
{ transform: `translate(${dx}px, ${dy}px) rotate(${rotate}deg) scale(${scale})`, opacity: 0 }
], {
duration: 1500 + Math.random()*1000,
easing: 'cubic-bezier(0.22, 1, 0.36, 1)'
});
setTimeout(() => particle.remove(), 2200);
}
});
});