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