`<!DOCTYPE html>
Interactive Particle System
<br>
* {<br>
margin: 0;<br>
padding: 0;<br>
box-sizing: border-box;<br>
}</p>
<div class="highlight"><pre class="highlight plaintext"><code> body {
font-family: 'Arial', sans-serif;
overflow: hidden;
background-color: #0a0a0a;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
color: white;
}
#canvas {
position: fixed;
top: 0;
left: 0;
z-index: 1;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 2;
pointer-events: none;
}
.title {
font-size: 2.5rem;
font-weight: bold;
text-shadow: 0 0 10px rgba(139, 92, 246, 0.8);
color: #8b5cf6;
opacity: 0;
transition: opacity 2s ease;
margin-bottom: 20px;
text-align: center;
}
.subtitle {
font-size: 1.2rem;
opacity: 0;
transition: opacity 2s ease;
text-align: center;
max-width: 80%;
text-shadow: 0 0 5px rgba(255, 255, 255, 0.5);
}
.cta {
position: absolute;
bottom: 5%;
font-size: 1.4rem;
font-weight: bold;
color: #8b5cf6;
opacity: 0;
transition: opacity 1s ease;
text-align: center;
z-index: 5;
width: 100%;
text-shadow: 0 0 10px rgba(139, 92, 246, 0.8);
}
.overlay-touch {
position: absolute;
width: 100%;
height: 100%;
z-index: 10;
opacity: 0;
pointer-events: all;
cursor: pointer;
}
</style>
</code></pre></div>
<p></head><br>
<body><br>
<canvas id="canvas"></canvas></p>
<div class="highlight"><pre class="highlight plaintext"><code><!-- <div class="overlay">
<h1 class="title">Interactive Particle System</h1>
<h2 class="subtitle">Touch or click to interact with particles</h2>
</div> -->
<div class="cta">Double tap to create particle explosions</div>
<div class="overlay-touch"></div>
<script>
// Canvas setup
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Animation variables
let particles = [];
let connections = [];
let animationStage = 0;
let lastTime = 0;
let mouseX = 0, mouseY = 0;
let isUserInteracting = false;
let isFirstTouch = true;
// DOM elements
const title = document.querySelector('.title');
const subtitle = document.querySelector('.subtitle');
const cta = document.querySelector('.cta');
const touchOverlay = document.querySelector('.overlay-touch');
// Colors
const colors = [
'#8b5cf6', // Purple
'#3b82f6', // Blue
'#ec4899', // Pink
'#10b981', // Green
'#f59e0b' // Amber
];
// Particle class
class Particle {
constructor(x, y, color, size = 3, speedFactor = 1, fixed = false) {
this.x = x;
this.y = y;
this.size = size;
this.color = color;
this.baseX = x;
this.baseY = y;
this.density = (Math.random() * 30) + 1;
this.speedFactor = speedFactor;
this.fixed = fixed;
this.vx = (Math.random() - 0.5) * 2;
this.vy = (Math.random() - 0.5) * 2;
this.friction = 0.95;
this.life = 1;
this.decay = Math.random() * 0.01 + 0.001;
this.maxSize = size;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size * this.life, 0, Math.PI * 2);
ctx.closePath();
// Create gradient for particle
const gradient = ctx.createRadialGradient(
this.x, this.y, 0,
this.x, this.y, this.size * this.life
);
gradient.addColorStop(0, this.color);
gradient.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = gradient;
ctx.fill();
}
update() {
if (this.fixed) return;
// Apply velocity
this.x += this.vx * this.speedFactor;
this.y += this.vy * this.speedFactor;
// Apply friction
this.vx *= this.friction;
this.vy *= this.friction;
// Apply gravity (subtle pull downward)
this.vy += 0.02 * this.speedFactor;
// Boundary checks with bounce effect
if (this.x &lt; this.size) {
this.x = this.size;
this.vx *= -0.8;
}
if (this.x &gt; canvas.width - this.size) {
this.x = canvas.width - this.size;
this.vx *= -0.8;
}
if (this.y &lt; this.size) {
this.y = this.size;
this.vy *= -0.8;
}
if (this.y &gt; canvas.height - this.size) {
this.y = canvas.height - this.size;
this.vy *= -0.8;
}
// Mouse interaction
if (isUserInteracting) {
const dx = mouseX - this.x;
const dy = mouseY - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const forceDirectionX = dx / distance;
const forceDirectionY = dy / distance;
// Calculate force (stronger when closer)
const maxDistance = 150;
const force = (maxDistance - Math.min(maxDistance, distance)) / 10;
if (distance &lt; maxDistance) {
this.vx += forceDirectionX * force / this.density;
this.vy += forceDirectionY * force / this.density;
}
}
// Decay particle life if it's an explosion particle
if (this.decay &gt; 0) {
this.life -= this.decay;
if (this.life &lt;= 0) {
this.life = 0;
}
}
}
isAlive() {
return this.life &gt; 0.01;
}
}
// Create initial particles
function initParticles() {
particles = [];
for (let i = 0; i &lt; 150; i++) {
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
const color = colors[Math.floor(Math.random() * colors.length)];
const size = Math.random() * 3 + 2;
particles.push(new Particle(x, y, color, size));
}
}
// Draw connections between particles
function connectParticles() {
connections = [];
const maxDistance = 150;
for (let i = 0; i &lt; particles.length; i++) {
if (!particles[i].isAlive()) continue;
for (let j = i + 1; j &lt; particles.length; j++) {
if (!particles[j].isAlive()) continue;
const dx = particles[i].x - particles[j].x;
const dy = particles[i].y - particles[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance &lt; maxDistance) {
const opacity = 1 - (distance / maxDistance);
connections.push({
start: particles[i],
end: particles[j],
opacity: opacity * particles[i].life * particles[j].life
});
}
}
}
}
// Draw the connections
function drawConnections() {
for (const connection of connections) {
ctx.beginPath();
ctx.moveTo(connection.start.x, connection.start.y);
ctx.lineTo(connection.end.x, connection.end.y);
const gradient = ctx.createLinearGradient(
connection.start.x, connection.start.y,
connection.end.x, connection.end.y
);
gradient.addColorStop(0, connection.start.color);
gradient.addColorStop(1, connection.end.color);
ctx.strokeStyle = gradient;
ctx.globalAlpha = connection.opacity * 0.7;
ctx.lineWidth = connection.opacity * 2;
ctx.stroke();
ctx.globalAlpha = 1;
}
}
// Create particle explosion
function createExplosion(x, y) {
const particleCount = Math.floor(Math.random() * 20) + 30;
const baseHue = Math.floor(Math.random() * 360);
for (let i = 0; i &lt; particleCount; i++) {
// Create color variations
const hue = (baseHue + Math.random() * 30 - 15) % 360;
const color = `hsl(${hue}, 80%, 60%)`;
// Calculate random direction
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 6 + 1;
const size = Math.random() * 4 + 2;
const particle = new Particle(x, y, color, size);
particle.vx = Math.cos(angle) * speed;
particle.vy = Math.sin(angle) * speed;
particle.friction = 0.98;
particle.decay = Math.random() * 0.02 + 0.005;
particles.push(particle);
}
}
// Animation loop
function animate(timestamp) {
const deltaTime = timestamp - lastTime;
lastTime = timestamp;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update and draw particles
particles = particles.filter(particle =&gt; particle.isAlive());
for (const particle of particles) {
particle.update();
particle.draw();
}
// Connect particles
connectParticles();
drawConnections();
// Progress the animation stages
updateAnimation(deltaTime);
requestAnimationFrame(animate);
}
// Handle animation stages
function updateAnimation(deltaTime) {
if (animationStage === 0 &amp;&amp; lastTime &gt; 1000) {
// Show title
title.style.opacity = '1';
setTimeout(() =&gt; {
subtitle.style.opacity = '1';
touchOverlay.style.opacity = '1';
animationStage = 1;
}, 2000);
}
else if (animationStage === 1 &amp;&amp; lastTime &gt; 5000) {
// Show CTA
cta.style.opacity = '1';
animationStage = 2;
// Create random explosions occasionally
setInterval(() =&gt; {
if (!isUserInteracting &amp;&amp; Math.random() &lt; 0.3) {
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
createExplosion(x, y);
}
}, 3000);
}
}
// Handle window resize
window.addEventListener('resize', () =&gt; {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
initParticles();
});
// Handle mouse/touch events for user interaction
touchOverlay.addEventListener('mousemove', (e) =&gt; {
mouseX = e.clientX;
mouseY = e.clientY;
});
touchOverlay.addEventListener('mousedown', () =&gt; {
isUserInteracting = true;
});
touchOverlay.addEventListener('mouseup', () =&gt; {
isUserInteracting = false;
});
touchOverlay.addEventListener('touchmove', (e) =&gt; {
e.preventDefault();
mouseX = e.touches[0].clientX;
mouseY = e.touches[0].clientY;
isUserInteracting = true;
// Create small particles while dragging
if (Math.random() &lt; 0.3) {
const color = colors[Math.floor(Math.random() * colors.length)];
const particle = new Particle(mouseX, mouseY, color, Math.random() * 2 + 1);
particle.vx = (Math.random() - 0.5) * 2;
particle.vy = (Math.random() - 0.5) * 2;
particles.push(particle);
}
});
touchOverlay.addEventListener('touchend', () =&gt; {
isUserInteracting = false;
});
// Create explosion on click/tap
touchOverlay.addEventListener('click', (e) =&gt; {
const x = e.clientX || e.touches[0].clientX;
const y = e.clientY || e.touches[0].clientY;
createExplosion(x, y);
// Show/hide UI elements based on first touch
if (isFirstTouch) {
setTimeout(() =&gt; {
title.style.opacity = '0';
subtitle.style.opacity = '0';
}, 500);
isFirstTouch = false;
}
});
// Initialize and start animation
function init() {
initParticles();
requestAnimationFrame(animate);
}
init();
</script>
</code></pre></div>
<p></body><br>
</html>`</p>
Top comments (0)