Skip to content

Commit c4019ce

Browse files
committed
feat: Add neural network background animation and integrate Vite for build process.
1 parent a82e759 commit c4019ce

File tree

6 files changed

+324
-80
lines changed

6 files changed

+324
-80
lines changed

index.html

Lines changed: 95 additions & 63 deletions
Large diffs are not rendered by default.

pnpm-lock.yaml

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/neural-network.js

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
class NeuralNetwork {
2+
constructor() {
3+
this.canvas = document.getElementById('neural-network-bg');
4+
this.ctx = this.canvas.getContext('2d');
5+
this.particles = [];
6+
this.pulses = []; // Array to store active pulses
7+
this.particleCount = 100;
8+
this.connectionDistance = 150;
9+
this.mouseDistance = 200;
10+
11+
this.mouse = {
12+
x: null,
13+
y: null
14+
};
15+
16+
this.init();
17+
this.animate();
18+
19+
window.addEventListener('resize', () => this.resize());
20+
window.addEventListener('mousemove', (e) => this.handleMouseMove(e));
21+
window.addEventListener('mouseout', () => this.handleMouseOut());
22+
}
23+
24+
init() {
25+
this.resize();
26+
this.createParticles();
27+
}
28+
29+
resize() {
30+
this.canvas.width = window.innerWidth;
31+
this.canvas.height = window.innerHeight;
32+
this.createParticles(); // Recreate particles on resize to maintain density
33+
}
34+
35+
createParticles() {
36+
this.particles = [];
37+
// Reduced density for a cleaner look (larger divisor)
38+
const area = this.canvas.width * this.canvas.height;
39+
this.particleCount = Math.floor(area / 25000);
40+
41+
for (let i = 0; i < this.particleCount; i++) {
42+
this.particles.push({
43+
x: Math.random() * this.canvas.width,
44+
y: Math.random() * this.canvas.height,
45+
// Much slower velocity for a professional, calm feel
46+
vx: (Math.random() - 0.5) * 0.3,
47+
vy: (Math.random() - 0.5) * 0.3,
48+
size: Math.random() * 1.5 + 0.5, // Smaller particles
49+
color: '#00f2ff'
50+
});
51+
}
52+
}
53+
54+
handleMouseMove(e) {
55+
this.mouse.x = e.x;
56+
this.mouse.y = e.y;
57+
}
58+
59+
handleMouseOut() {
60+
this.mouse.x = null;
61+
this.mouse.y = null;
62+
}
63+
64+
animate() {
65+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
66+
67+
// Update and draw particles
68+
for (let i = 0; i < this.particles.length; i++) {
69+
const p = this.particles[i];
70+
71+
// Move
72+
p.x += p.vx;
73+
p.y += p.vy;
74+
75+
// Bounce off edges
76+
if (p.x < 0 || p.x > this.canvas.width) p.vx *= -1;
77+
if (p.y < 0 || p.y > this.canvas.height) p.vy *= -1;
78+
79+
// Mouse interaction
80+
if (this.mouse.x != null) {
81+
const dx = this.mouse.x - p.x;
82+
const dy = this.mouse.y - p.y;
83+
const distance = Math.sqrt(dx * dx + dy * dy);
84+
85+
if (distance < this.mouseDistance) {
86+
const forceDirectionX = dx / distance;
87+
const forceDirectionY = dy / distance;
88+
const force = (this.mouseDistance - distance) / this.mouseDistance;
89+
// Gentler interaction
90+
const directionX = forceDirectionX * force * 0.05;
91+
const directionY = forceDirectionY * force * 0.05;
92+
93+
p.vx += directionX;
94+
p.vy += directionY;
95+
}
96+
}
97+
98+
// Draw particle
99+
this.ctx.beginPath();
100+
this.ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
101+
this.ctx.fillStyle = `rgba(0, 242, 255, 0.5)`; // Semi-transparent particles
102+
this.ctx.fill();
103+
104+
// Connect particles
105+
for (let j = i; j < this.particles.length; j++) {
106+
const p2 = this.particles[j];
107+
const dx = p.x - p2.x;
108+
const dy = p.y - p2.y;
109+
const distance = Math.sqrt(dx * dx + dy * dy);
110+
111+
if (distance < this.connectionDistance) {
112+
this.ctx.beginPath();
113+
// Subtler connections (lower opacity)
114+
const opacity = (1 - distance / this.connectionDistance) * 0.15;
115+
this.ctx.strokeStyle = `rgba(0, 242, 255, ${opacity})`;
116+
this.ctx.lineWidth = 1;
117+
this.ctx.moveTo(p.x, p.y);
118+
this.ctx.lineTo(p2.x, p2.y);
119+
this.ctx.stroke();
120+
121+
// Randomly spawn a pulse on this connection
122+
if (Math.random() < 0.0005) { // Low probability for subtlety
123+
this.pulses.push({
124+
x: p.x,
125+
y: p.y,
126+
targetX: p2.x,
127+
targetY: p2.y,
128+
progress: 0,
129+
speed: 0.02 + Math.random() * 0.03 // Random speed
130+
});
131+
}
132+
}
133+
}
134+
}
135+
136+
// Update and draw pulses
137+
for (let i = this.pulses.length - 1; i >= 0; i--) {
138+
const pulse = this.pulses[i];
139+
pulse.progress += pulse.speed;
140+
141+
if (pulse.progress >= 1) {
142+
this.pulses.splice(i, 1); // Remove finished pulses
143+
continue;
144+
}
145+
146+
const currentX = pulse.x + (pulse.targetX - pulse.x) * pulse.progress;
147+
const currentY = pulse.y + (pulse.targetY - pulse.y) * pulse.progress;
148+
149+
this.ctx.beginPath();
150+
this.ctx.arc(currentX, currentY, 1.5, 0, Math.PI * 2);
151+
this.ctx.fillStyle = 'rgba(0, 242, 255, 0.8)'; // Bright pulse
152+
this.ctx.fill();
153+
}
154+
155+
requestAnimationFrame(() => this.animate());
156+
}
157+
}
158+
159+
// Initialize when DOM is loaded
160+
document.addEventListener('DOMContentLoaded', () => {
161+
new NeuralNetwork();
162+
});

src/script.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
document.getElementById('year').textContent = new Date().getFullYear();
2+
3+
// Subtle Neural Network Animation
4+
function createNeuralNetwork() {
5+
const container = document.getElementById('neuralNetwork');
6+
const nodeCount = 8;
7+
const nodes = [];
8+
9+
// Create subtle nodes
10+
for (let i = 0; i < nodeCount; i++) {
11+
const node = document.createElement('div');
12+
node.className = 'neural-node';
13+
node.style.left = Math.random() * 90 + 5 + '%';
14+
node.style.top = Math.random() * 90 + 5 + '%';
15+
node.style.animationDelay = Math.random() * 4 + 's';
16+
container.appendChild(node);
17+
nodes.push(node);
18+
}
19+
20+
// Create subtle connections
21+
nodes.forEach((node, i) => {
22+
if (i < nodes.length - 1 && Math.random() > 0.3) {
23+
const connection = document.createElement('div');
24+
connection.className = 'neural-connection';
25+
26+
const x1 = parseFloat(node.style.left);
27+
const y1 = parseFloat(node.style.top);
28+
const x2 = parseFloat(nodes[i + 1].style.left);
29+
const y2 = parseFloat(nodes[i + 1].style.top);
30+
31+
const deltaX = x2 - x1;
32+
const deltaY = y2 - y1;
33+
const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 8;
34+
const angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI;
35+
36+
connection.style.left = x1 + '%';
37+
connection.style.top = y1 + '%';
38+
connection.style.width = length + 'px';
39+
connection.style.transform = `rotate(${angle}deg)`;
40+
connection.style.animationDelay = Math.random() * 6 + 's';
41+
42+
container.appendChild(connection);
43+
}
44+
});
45+
}
46+
47+
// Initialize neural network
48+
createNeuralNetwork();

src/styles.css

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,14 @@ body {
2525
overflow-x: hidden;
2626
}
2727

28-
.animated-bg {
28+
#neural-network-bg {
2929
position: fixed;
30-
width: 100%;
31-
height: 100%;
3230
top: 0;
3331
left: 0;
32+
width: 100%;
33+
height: 100%;
3434
z-index: -1;
35-
background: radial-gradient(circle at 50% 50%, rgba(0, 242, 255, 0.08), transparent 40%),
36-
radial-gradient(ellipse at top, rgba(121, 40, 202, 0.12), transparent),
37-
radial-gradient(ellipse at bottom, rgba(0, 242, 255, 0.1), transparent);
35+
background: var(--darker);
3836
}
3937

4038
.grid-overlay {
@@ -53,11 +51,13 @@ body {
5351
}
5452

5553
@keyframes gridFloat {
54+
5655
0%,
5756
100% {
5857
transform: translate(0, 0) scale(1);
5958
opacity: 0.4;
6059
}
60+
6161
50% {
6262
transform: translate(30px, 30px) scale(1.05);
6363
opacity: 0.6;
@@ -123,10 +123,12 @@ section {
123123
}
124124

125125
@keyframes bounce {
126+
126127
0%,
127128
100% {
128129
transform: translateY(0);
129130
}
131+
130132
50% {
131133
transform: translateY(10px);
132134
}
@@ -416,6 +418,8 @@ footer {
416418
opacity: 0.8;
417419
}
418420

421+
422+
419423
@media (max-width: 768px) {
420424
section {
421425
padding: 2rem 1rem;
@@ -500,4 +504,4 @@ footer {
500504
font-size: 0.75rem;
501505
padding: 0.3rem 0.8rem;
502506
}
503-
}
507+
}

vite.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from 'vite'
2+
3+
export default defineConfig({
4+
build: {
5+
outDir: 'dist',
6+
emptyOutDir: true
7+
}
8+
})

0 commit comments

Comments
 (0)