draft demo-02
This commit is contained in:
parent
59fa0315f1
commit
ae8c09013c
3 changed files with 291 additions and 0 deletions
8
demo-02/README.md
Normal file
8
demo-02/README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Demo 2: Fixing an Issue, Adding a new Feature
|
||||||
|
|
||||||
|
- `demo-01.index.html` has the output of a former run of `../demo1`
|
||||||
|
- There is an issue where the background flickers as the last few particles
|
||||||
|
fade out. Smooth out the rapid color change.
|
||||||
|
- Add a new feature: clicking should cause a burst of particles as well as
|
||||||
|
a shift in the overall hue.
|
||||||
|
- The output of a previous plan is in `old-plan.md`.
|
||||||
227
demo-02/demo-01.index.html
Normal file
227
demo-02/demo-01.index.html
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Particle Trail Effect</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 4rem;
|
||||||
|
font-weight: 300;
|
||||||
|
letter-spacing: 0.3em;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-shadow: 0 0 30px rgba(100, 200, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="particleCanvas"></canvas>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<h1>Particle Trail</h1>
|
||||||
|
<p>Move your cursor to create the magic</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const canvas = document.getElementById('particleCanvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Set canvas to full window size
|
||||||
|
function resizeCanvas() {
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
}
|
||||||
|
resizeCanvas();
|
||||||
|
window.addEventListener('resize', resizeCanvas);
|
||||||
|
|
||||||
|
// Mouse position tracking
|
||||||
|
const mouse = {
|
||||||
|
x: undefined,
|
||||||
|
y: undefined,
|
||||||
|
prevX: undefined,
|
||||||
|
prevY: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('mousemove', (e) => {
|
||||||
|
mouse.prevX = mouse.x;
|
||||||
|
mouse.prevY = mouse.y;
|
||||||
|
mouse.x = e.clientX;
|
||||||
|
mouse.y = e.clientY;
|
||||||
|
|
||||||
|
// Spawn particles on mouse move
|
||||||
|
if (mouse.prevX !== undefined) {
|
||||||
|
const dx = mouse.x - mouse.prevX;
|
||||||
|
const dy = mouse.y - mouse.prevY;
|
||||||
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
// Spawn more particles for faster movement
|
||||||
|
const particleCount = Math.min(Math.floor(distance / 2), 10);
|
||||||
|
for (let i = 0; i < particleCount; i++) {
|
||||||
|
const t = i / particleCount;
|
||||||
|
const x = mouse.prevX + dx * t;
|
||||||
|
const y = mouse.prevY + dy * t;
|
||||||
|
particles.push(new Particle(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Touch support
|
||||||
|
window.addEventListener('touchmove', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const touch = e.touches[0];
|
||||||
|
mouse.prevX = mouse.x;
|
||||||
|
mouse.prevY = mouse.y;
|
||||||
|
mouse.x = touch.clientX;
|
||||||
|
mouse.y = touch.clientY;
|
||||||
|
|
||||||
|
if (mouse.prevX !== undefined) {
|
||||||
|
const dx = mouse.x - mouse.prevX;
|
||||||
|
const dy = mouse.y - mouse.prevY;
|
||||||
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
const particleCount = Math.min(Math.floor(distance / 2), 10);
|
||||||
|
for (let i = 0; i < particleCount; i++) {
|
||||||
|
const t = i / particleCount;
|
||||||
|
const x = mouse.prevX + dx * t;
|
||||||
|
const y = mouse.prevY + dy * t;
|
||||||
|
particles.push(new Particle(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, { passive: false });
|
||||||
|
|
||||||
|
// Particle class
|
||||||
|
class Particle {
|
||||||
|
constructor(x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.size = Math.random() * 4 + 2;
|
||||||
|
this.baseSize = this.size;
|
||||||
|
|
||||||
|
// Random velocity for inertia effect
|
||||||
|
const angle = Math.random() * Math.PI * 2;
|
||||||
|
const speed = Math.random() * 2 + 0.5;
|
||||||
|
this.vx = Math.cos(angle) * speed;
|
||||||
|
this.vy = Math.sin(angle) * speed;
|
||||||
|
|
||||||
|
// Color with variation
|
||||||
|
const hue = Math.random() * 60 + 180; // Blue-cyan range
|
||||||
|
this.color = `hsla(${hue}, 100%, 70%,`;
|
||||||
|
|
||||||
|
this.life = 1; // Full life
|
||||||
|
this.decay = Math.random() * 0.02 + 0.01; // Random decay rate
|
||||||
|
}
|
||||||
|
|
||||||
|
update() {
|
||||||
|
// Apply inertia - particles continue moving
|
||||||
|
this.x += this.vx;
|
||||||
|
this.y += this.vy;
|
||||||
|
|
||||||
|
// Slow down over time (friction)
|
||||||
|
this.vx *= 0.98;
|
||||||
|
this.vy *= 0.98;
|
||||||
|
|
||||||
|
// Shrink size
|
||||||
|
this.size = this.baseSize * this.life;
|
||||||
|
|
||||||
|
// Fade out
|
||||||
|
this.life -= this.decay;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = this.color + this.life + ')';
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Add glow effect
|
||||||
|
ctx.shadowBlur = 15;
|
||||||
|
ctx.shadowColor = this.color + '0.5)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Particles array
|
||||||
|
let particles = [];
|
||||||
|
|
||||||
|
// Animation loop
|
||||||
|
function animate() {
|
||||||
|
// Clear with slight fade effect for trails
|
||||||
|
ctx.fillStyle = 'rgba(26, 26, 46, 0.1)';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Reset shadow for better performance
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
|
||||||
|
// Update and draw particles
|
||||||
|
for (let i = particles.length - 1; i >= 0; i--) {
|
||||||
|
const particle = particles[i];
|
||||||
|
particle.update();
|
||||||
|
particle.draw();
|
||||||
|
|
||||||
|
// Remove dead particles
|
||||||
|
if (particle.life <= 0) {
|
||||||
|
particles.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit particles for performance
|
||||||
|
if (particles.length > 500) {
|
||||||
|
particles.splice(0, particles.length - 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start animation
|
||||||
|
animate();
|
||||||
|
|
||||||
|
// Spawn some initial particles for visual interest
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
const x = Math.random() * canvas.width;
|
||||||
|
const y = Math.random() * canvas.height;
|
||||||
|
const particle = new Particle(x, y);
|
||||||
|
particle.life = Math.random() * 0.5 + 0.5;
|
||||||
|
particles.push(particle);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
56
demo-02/old-plan.md
Normal file
56
demo-02/old-plan.md
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
# Particle Burst Feature Plan
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Add a click interaction that creates a radial burst of particles and shifts the color palette.
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
### 1. Global State Addition
|
||||||
|
Add a `hueOffset` variable to track the current color shift:
|
||||||
|
```javascript
|
||||||
|
let hueOffset = 0; // Shifts the particle color palette
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Click Event Handler
|
||||||
|
Add to the script (after the mousemove listener):
|
||||||
|
```javascript
|
||||||
|
canvas.addEventListener('click', (e) => {
|
||||||
|
const clickX = e.clientX;
|
||||||
|
const clickY = e.clientY;
|
||||||
|
|
||||||
|
// Shift hue for variety
|
||||||
|
hueOffset += 15 + Math.random() * 15; // Shift by 15-30 degrees
|
||||||
|
|
||||||
|
// Create burst of particles
|
||||||
|
const burstCount = 40;
|
||||||
|
for (let i = 0; i < burstCount; i++) {
|
||||||
|
particles.push(new BurstParticle(clickX, clickY, hueOffset));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. BurstParticle Class (or extend Particle)
|
||||||
|
Create particles with radial velocity:
|
||||||
|
- Position: click coordinates
|
||||||
|
- Angle: random 0 to 2π
|
||||||
|
- Speed: random range (e.g., 2-8) for varying distances
|
||||||
|
- Size: slightly larger than normal particles (6-12px)
|
||||||
|
- Decay: slightly faster for quick burst effect
|
||||||
|
|
||||||
|
### 4. Particle Constructor Update
|
||||||
|
Modify the `Particle` constructor to accept an optional hue offset:
|
||||||
|
```javascript
|
||||||
|
constructor(x, y, hueShift = 0) {
|
||||||
|
// ... existing code ...
|
||||||
|
const hue = Math.random() * 60 + 180 + hueShift; // Blue-cyan + shift
|
||||||
|
this.color = `hsla(${hue}, 100%, 70%,`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Changes
|
||||||
|
- **index.html**: Add click listener, hueOffset variable, and optional hueShift parameter to Particle
|
||||||
|
|
||||||
|
## Visual Effect
|
||||||
|
- Each click creates an explosion of blue-cyan particles at the cursor
|
||||||
|
- Subsequent clicks shift the color palette gradually (e.g., cyan → green → yellow)
|
||||||
|
- Trail effect continues to work with the new particles
|
||||||
Loading…
Add table
Add a link
Reference in a new issue