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);
	}
  });

});