Change the particle background into an Interactive Particle Network background with animations.
Browse files
script.js
CHANGED
|
@@ -76,29 +76,39 @@ function createParticleNetwork() {
|
|
| 76 |
constructor() {
|
| 77 |
this.x = Math.random() * canvas.width;
|
| 78 |
this.y = Math.random() * canvas.height;
|
| 79 |
-
this.size = Math.random() *
|
| 80 |
this.baseX = this.x;
|
| 81 |
this.baseY = this.y;
|
| 82 |
-
this.density = (Math.random() *
|
| 83 |
this.color = this.getRandomColor();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
}
|
| 85 |
|
| 86 |
getRandomColor() {
|
| 87 |
-
const
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
'rgba(0, 255, 255, 0.8)', // cyan
|
| 92 |
-
'rgba(255, 255, 255, 0.8)' // white
|
| 93 |
-
];
|
| 94 |
-
return colors[Math.floor(Math.random() * colors.length)];
|
| 95 |
}
|
| 96 |
|
| 97 |
draw() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
ctx.beginPath();
|
| 99 |
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
|
| 100 |
-
ctx.
|
| 101 |
-
ctx.fillStyle = this.color;
|
| 102 |
ctx.fill();
|
| 103 |
|
| 104 |
// Add glow effect
|
|
@@ -107,44 +117,55 @@ function createParticleNetwork() {
|
|
| 107 |
}
|
| 108 |
|
| 109 |
update() {
|
| 110 |
-
//
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
const forceDirectionX = dx / distance;
|
| 115 |
-
const forceDirectionY = dy / distance;
|
| 116 |
-
const maxDistance = mouse.radius;
|
| 117 |
-
let force = (maxDistance - distance) / maxDistance;
|
| 118 |
-
|
| 119 |
-
if (force < 0) force = 0;
|
| 120 |
-
|
| 121 |
-
const directionX = forceDirectionX * force * this.density * 0.6;
|
| 122 |
-
const directionY = forceDirectionY * force * this.density * 0.6;
|
| 123 |
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
const
|
| 135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
}
|
| 138 |
|
| 139 |
// Add some random movement
|
| 140 |
-
this.x +=
|
| 141 |
-
this.y +=
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
}
|
| 143 |
}
|
| 144 |
|
| 145 |
// Create particles
|
| 146 |
const particles = [];
|
| 147 |
-
const particleCount = Math.floor((canvas.width * canvas.height) /
|
| 148 |
|
| 149 |
for (let i = 0; i < particleCount; i++) {
|
| 150 |
particles.push(new Particle());
|
|
@@ -158,46 +179,64 @@ function createParticleNetwork() {
|
|
| 158 |
const dy = particles[a].y - particles[b].y;
|
| 159 |
const distance = Math.sqrt(dx * dx + dy * dy);
|
| 160 |
|
| 161 |
-
if (distance <
|
| 162 |
-
const opacity = 1 - distance /
|
| 163 |
-
|
| 164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
ctx.beginPath();
|
| 166 |
ctx.moveTo(particles[a].x, particles[a].y);
|
| 167 |
ctx.lineTo(particles[b].x, particles[b].y);
|
| 168 |
ctx.stroke();
|
| 169 |
-
|
| 170 |
-
// Alternate line color
|
| 171 |
-
if (distance < 50) {
|
| 172 |
-
ctx.strokeStyle = `rgba(255, 0, 255, ${opacity * 0.3})`;
|
| 173 |
-
ctx.beginPath();
|
| 174 |
-
ctx.moveTo(particles[a].x, particles[a].y);
|
| 175 |
-
ctx.lineTo(particles[b].x, particles[b].y);
|
| 176 |
-
ctx.stroke();
|
| 177 |
-
}
|
| 178 |
}
|
| 179 |
}
|
| 180 |
}
|
| 181 |
}
|
| 182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
// Animation loop
|
| 184 |
function animate() {
|
| 185 |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 186 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
// Draw particles
|
| 188 |
for (let i = 0; i < particles.length; i++) {
|
| 189 |
particles[i].draw();
|
| 190 |
particles[i].update();
|
| 191 |
}
|
| 192 |
|
| 193 |
-
// Connect particles
|
| 194 |
-
connect();
|
| 195 |
-
|
| 196 |
requestAnimationFrame(animate);
|
| 197 |
}
|
| 198 |
|
| 199 |
// Handle resize
|
| 200 |
-
|
| 201 |
canvas.width = container.offsetWidth;
|
| 202 |
canvas.height = container.offsetHeight;
|
| 203 |
|
|
@@ -206,8 +245,18 @@ function createParticleNetwork() {
|
|
| 206 |
for (let i = 0; i < particleCount; i++) {
|
| 207 |
particles.push(new Particle());
|
| 208 |
}
|
| 209 |
-
}
|
|
|
|
|
|
|
| 210 |
|
| 211 |
// Start animation
|
| 212 |
animate();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
}
|
|
|
|
| 76 |
constructor() {
|
| 77 |
this.x = Math.random() * canvas.width;
|
| 78 |
this.y = Math.random() * canvas.height;
|
| 79 |
+
this.size = Math.random() * 2 + 1;
|
| 80 |
this.baseX = this.x;
|
| 81 |
this.baseY = this.y;
|
| 82 |
+
this.density = (Math.random() * 50) + 10;
|
| 83 |
this.color = this.getRandomColor();
|
| 84 |
+
this.velocity = {
|
| 85 |
+
x: (Math.random() - 0.5) * 0.5,
|
| 86 |
+
y: (Math.random() - 0.5) * 0.5
|
| 87 |
+
};
|
| 88 |
+
this.angle = Math.random() * Math.PI * 2;
|
| 89 |
+
this.orbitRadius = Math.random() * 50 + 20;
|
| 90 |
+
this.orbitSpeed = Math.random() * 0.02 + 0.005;
|
| 91 |
}
|
| 92 |
|
| 93 |
getRandomColor() {
|
| 94 |
+
const hue = Math.random() * 360;
|
| 95 |
+
const saturation = 80 + Math.random() * 20;
|
| 96 |
+
const lightness = 50 + Math.random() * 20;
|
| 97 |
+
return `hsla(${hue}, ${saturation}%, ${lightness}%, 0.8)`;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
}
|
| 99 |
|
| 100 |
draw() {
|
| 101 |
+
// Create gradient for each particle
|
| 102 |
+
const gradient = ctx.createRadialGradient(
|
| 103 |
+
this.x, this.y, 0,
|
| 104 |
+
this.x, this.y, this.size
|
| 105 |
+
);
|
| 106 |
+
gradient.addColorStop(0, this.color);
|
| 107 |
+
gradient.addColorStop(1, 'transparent');
|
| 108 |
+
|
| 109 |
ctx.beginPath();
|
| 110 |
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
|
| 111 |
+
ctx.fillStyle = gradient;
|
|
|
|
| 112 |
ctx.fill();
|
| 113 |
|
| 114 |
// Add glow effect
|
|
|
|
| 117 |
}
|
| 118 |
|
| 119 |
update() {
|
| 120 |
+
// Orbital movement
|
| 121 |
+
this.angle += this.orbitSpeed;
|
| 122 |
+
this.baseX = canvas.width/2 + Math.cos(this.angle) * this.orbitRadius;
|
| 123 |
+
this.baseY = canvas.height/2 + Math.sin(this.angle) * this.orbitRadius;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
+
// Mouse interaction
|
| 126 |
+
if (mouse.x && mouse.y) {
|
| 127 |
+
const dx = mouse.x - this.x;
|
| 128 |
+
const dy = mouse.y - this.y;
|
| 129 |
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
| 130 |
+
const maxDistance = mouse.radius;
|
| 131 |
+
|
| 132 |
+
if (distance < maxDistance) {
|
| 133 |
+
const forceDirectionX = dx / distance;
|
| 134 |
+
const forceDirectionY = dy / distance;
|
| 135 |
+
const force = (maxDistance - distance) / maxDistance;
|
| 136 |
+
const directionX = forceDirectionX * force * this.density * 0.6;
|
| 137 |
+
const directionY = forceDirectionY * force * this.density * 0.6;
|
| 138 |
+
|
| 139 |
+
this.x -= directionX;
|
| 140 |
+
this.y -= directionY;
|
| 141 |
+
} else {
|
| 142 |
+
// Return to original position with easing
|
| 143 |
+
if (this.x !== this.baseX) {
|
| 144 |
+
this.x += (this.baseX - this.x) / 20;
|
| 145 |
+
}
|
| 146 |
+
if (this.y !== this.baseY) {
|
| 147 |
+
this.y += (this.baseY - this.y) / 20;
|
| 148 |
+
}
|
| 149 |
}
|
| 150 |
+
} else {
|
| 151 |
+
// Continue orbiting if mouse not present
|
| 152 |
+
this.x += (this.baseX - this.x) / 20;
|
| 153 |
+
this.y += (this.baseY - this.y) / 20;
|
| 154 |
}
|
| 155 |
|
| 156 |
// Add some random movement
|
| 157 |
+
this.x += this.velocity.x;
|
| 158 |
+
this.y += this.velocity.y;
|
| 159 |
+
|
| 160 |
+
// Bounce off edges
|
| 161 |
+
if (this.x < 0 || this.x > canvas.width) this.velocity.x *= -1;
|
| 162 |
+
if (this.y < 0 || this.y > canvas.height) this.velocity.y *= -1;
|
| 163 |
}
|
| 164 |
}
|
| 165 |
|
| 166 |
// Create particles
|
| 167 |
const particles = [];
|
| 168 |
+
const particleCount = Math.floor((canvas.width * canvas.height) / 5000);
|
| 169 |
|
| 170 |
for (let i = 0; i < particleCount; i++) {
|
| 171 |
particles.push(new Particle());
|
|
|
|
| 179 |
const dy = particles[a].y - particles[b].y;
|
| 180 |
const distance = Math.sqrt(dx * dx + dy * dy);
|
| 181 |
|
| 182 |
+
if (distance < 150) {
|
| 183 |
+
const opacity = 1 - distance / 150;
|
| 184 |
+
|
| 185 |
+
// Create gradient for connection line
|
| 186 |
+
const gradient = ctx.createLinearGradient(
|
| 187 |
+
particles[a].x, particles[a].y,
|
| 188 |
+
particles[b].x, particles[b].y
|
| 189 |
+
);
|
| 190 |
+
gradient.addColorStop(0, particles[a].color);
|
| 191 |
+
gradient.addColorStop(1, particles[b].color);
|
| 192 |
+
|
| 193 |
+
ctx.strokeStyle = gradient;
|
| 194 |
+
ctx.globalAlpha = opacity * 0.3;
|
| 195 |
+
ctx.lineWidth = 0.5 + opacity * 1.5;
|
| 196 |
ctx.beginPath();
|
| 197 |
ctx.moveTo(particles[a].x, particles[a].y);
|
| 198 |
ctx.lineTo(particles[b].x, particles[b].y);
|
| 199 |
ctx.stroke();
|
| 200 |
+
ctx.globalAlpha = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
}
|
| 202 |
}
|
| 203 |
}
|
| 204 |
}
|
| 205 |
|
| 206 |
+
// Draw background
|
| 207 |
+
function drawBackground() {
|
| 208 |
+
const gradient = ctx.createRadialGradient(
|
| 209 |
+
canvas.width/2, canvas.height/2, 0,
|
| 210 |
+
canvas.width/2, canvas.height/2, Math.max(canvas.width, canvas.height)/2
|
| 211 |
+
);
|
| 212 |
+
gradient.addColorStop(0, 'rgba(5, 5, 15, 0)');
|
| 213 |
+
gradient.addColorStop(1, 'rgba(10, 10, 26, 1)');
|
| 214 |
+
|
| 215 |
+
ctx.fillStyle = gradient;
|
| 216 |
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
// Animation loop
|
| 220 |
function animate() {
|
| 221 |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 222 |
|
| 223 |
+
// Draw background
|
| 224 |
+
drawBackground();
|
| 225 |
+
|
| 226 |
+
// Connect particles first (behind)
|
| 227 |
+
connect();
|
| 228 |
+
|
| 229 |
// Draw particles
|
| 230 |
for (let i = 0; i < particles.length; i++) {
|
| 231 |
particles[i].draw();
|
| 232 |
particles[i].update();
|
| 233 |
}
|
| 234 |
|
|
|
|
|
|
|
|
|
|
| 235 |
requestAnimationFrame(animate);
|
| 236 |
}
|
| 237 |
|
| 238 |
// Handle resize
|
| 239 |
+
function handleResize() {
|
| 240 |
canvas.width = container.offsetWidth;
|
| 241 |
canvas.height = container.offsetHeight;
|
| 242 |
|
|
|
|
| 245 |
for (let i = 0; i < particleCount; i++) {
|
| 246 |
particles.push(new Particle());
|
| 247 |
}
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
window.addEventListener('resize', handleResize);
|
| 251 |
|
| 252 |
// Start animation
|
| 253 |
animate();
|
| 254 |
+
|
| 255 |
+
// Add periodic pulse effect
|
| 256 |
+
setInterval(() => {
|
| 257 |
+
particles.forEach(p => {
|
| 258 |
+
p.orbitSpeed = (Math.random() * 0.03 + 0.01) * (Math.random() > 0.5 ? 1 : -1);
|
| 259 |
+
p.orbitRadius = Math.random() * 100 + 50;
|
| 260 |
+
});
|
| 261 |
+
}, 3000);
|
| 262 |
}
|
style.css
CHANGED
|
@@ -257,7 +257,6 @@ box-shadow: 0 0 30px rgba(15, 240, 252, 0.5),
|
|
| 257 |
background: radial-gradient(circle at center, #050515 0%, #0a0a1a 100%);
|
| 258 |
overflow: hidden;
|
| 259 |
}
|
| 260 |
-
|
| 261 |
.cosmic-canvas {
|
| 262 |
position: absolute;
|
| 263 |
top: 0;
|
|
@@ -265,8 +264,9 @@ box-shadow: 0 0 30px rgba(15, 240, 252, 0.5),
|
|
| 265 |
width: 100%;
|
| 266 |
height: 100%;
|
| 267 |
z-index: 1;
|
| 268 |
-
opacity: 0.
|
| 269 |
mix-blend-mode: screen;
|
|
|
|
| 270 |
}
|
| 271 |
/* Brand Logo Positioning */
|
| 272 |
.brand-logo {
|
|
|
|
| 257 |
background: radial-gradient(circle at center, #050515 0%, #0a0a1a 100%);
|
| 258 |
overflow: hidden;
|
| 259 |
}
|
|
|
|
| 260 |
.cosmic-canvas {
|
| 261 |
position: absolute;
|
| 262 |
top: 0;
|
|
|
|
| 264 |
width: 100%;
|
| 265 |
height: 100%;
|
| 266 |
z-index: 1;
|
| 267 |
+
opacity: 0.8;
|
| 268 |
mix-blend-mode: screen;
|
| 269 |
+
pointer-events: none;
|
| 270 |
}
|
| 271 |
/* Brand Logo Positioning */
|
| 272 |
.brand-logo {
|