diff --git a/assets/lain.png b/assets/lain.png deleted file mode 100644 index 489087e..0000000 Binary files a/assets/lain.png and /dev/null differ diff --git a/js/matrix.js b/js/matrix.js deleted file mode 100644 index c70f0ad..0000000 --- a/js/matrix.js +++ /dev/null @@ -1,320 +0,0 @@ -class MatrixBackground { - constructor() { - this.canvas = document.getElementById('matrixCanvas'); - this.ctx = this.canvas.getContext('2d'); - this.dots = []; - this.connections = []; - this.mouse = { x: -9999, y: -9999, active: false }; - this.rafId = null; - this.isRunning = true; - this.prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; - - this.config = { - dotCount: this.getMobileDotCount(), - dotSpeed: 0.2, - connectionDuration: { min: 2000, max: 6000 }, - connectionChance: 0.0002, - maxConnections: this.getMobileMaxConnections(), - dotSize: 3, - mouseRadius: 110, - mouseForce: 0.6, - // Palette synced to the UI accent tokens - colors: { - dotDefault: 'rgba(230, 240, 255, 0.78)', - dotDefaultGlow: 'rgba(180, 220, 255, 0.40)', - dotConnected: 'rgba(120, 190, 245, 0.85)', - dotConnectedGlow: 'rgba(79, 195, 247, 0.55)', - connection: { - start: 'rgba(102, 126, 234, 0.22)', - middle: 'rgba(118, 75, 162, 0.62)', - end: 'rgba(79, 195, 247, 0.22)' - } - } - }; - - this.init(); - } - - getMobileDotCount() { - if (window.innerWidth <= 480) return 35; - if (window.innerWidth <= 768) return 45; - if (window.innerWidth >= 2100) return 100; - return 50; - } - - getMobileMaxConnections() { - if (window.innerWidth <= 480) return 4; - if (window.innerWidth <= 768) return 6; - return 8; - } - - init() { - this.handleResize(); - this.bindEvents(); - this.animate(); - } - - bindEvents() { - // Pointer reactivity — push dots away from cursor for a "matrix repel" feel. - // Touch devices are included; the dot pushes happen only while touched. - window.addEventListener('pointermove', (e) => { - this.mouse.x = e.clientX; - this.mouse.y = e.clientY; - this.mouse.active = true; - }, { passive: true }); - - window.addEventListener('pointerout', () => { - this.mouse.active = false; - this.mouse.x = -9999; - this.mouse.y = -9999; - }); - - // Pause the rAF loop when the tab is hidden to save CPU / battery. - document.addEventListener('visibilitychange', () => { - if (document.hidden) { - this.stop(); - } else { - this.start(); - } - }); - - // Resize is debounced via rAF; disabled on mobile for perf parity with original. - if (window.innerWidth > 768) { - let resizeRaf = null; - window.addEventListener('resize', () => { - if (resizeRaf) cancelAnimationFrame(resizeRaf); - resizeRaf = requestAnimationFrame(() => this.handleResize()); - }); - } - } - - start() { - if (this.rafId || !this.isRunning) return; - this.animate(); - } - - stop() { - if (this.rafId) { - cancelAnimationFrame(this.rafId); - this.rafId = null; - } - } - - handleResize() { - this.canvas.width = window.innerWidth; - this.canvas.height = window.innerHeight; - this.config.dotCount = this.getMobileDotCount(); - this.config.maxConnections = this.getMobileMaxConnections(); - this.createDots(); - } - - createDots() { - this.dots = []; - for (let i = 0; i < this.config.dotCount; i++) { - this.createDot(); - } - } - - createDot() { - const startX = Math.random() * (this.canvas.width - 20) + 10; - const startY = Math.random() * (this.canvas.height - 20) + 10; - - const angle = Math.random() * Math.PI * 2; - const speed = this.config.dotSpeed * (0.8 + Math.random() * 0.4); - const baseVelocityX = Math.cos(angle) * speed; - const baseVelocityY = Math.sin(angle) * speed; - - this.dots.push({ - x: startX, - y: startY, - vx: baseVelocityX, - vy: baseVelocityY, - baseVx: baseVelocityX, - baseVy: baseVelocityY, - wanderPhase: Math.random() * Math.PI * 2, - wanderSpeed: 0.01 + Math.random() * 0.01, - opacity: Math.random() * 0.3 + 0.7, - size: this.config.dotSize + Math.random() * 2, - connectionCount: 0 - }); - } - - updateDots() { - const radius = this.config.mouseRadius; - const radiusSq = radius * radius; - const force = this.config.mouseForce; - - this.dots.forEach(dot => { - // Mouse repulsion - if (this.mouse.active) { - const dx = dot.x - this.mouse.x; - const dy = dot.y - this.mouse.y; - const distSq = dx * dx + dy * dy; - if (distSq < radiusSq && distSq > 0.5) { - const dist = Math.sqrt(distSq); - const strength = (1 - dist / radius) * force; - dot.vx += (dx / dist) * strength; - dot.vy += (dy / dist) * strength; - } - } - - dot.x += dot.vx; - dot.y += dot.vy; - - // Ease back to a gentle autonomous drift after pointer interaction. - dot.wanderPhase += dot.wanderSpeed; - const wanderX = Math.cos(dot.wanderPhase) * 0.03; - const wanderY = Math.sin(dot.wanderPhase * 0.8) * 0.03; - dot.vx += (dot.baseVx + wanderX - dot.vx) * 0.02; - dot.vy += (dot.baseVy + wanderY - dot.vy) * 0.02; - - // Reflect at the canvas edge. Keep the base velocity in sync with - // the bounce so the drift easing does not push the dot back into - // the same wall on the next frames. - const dotRadius = dot.size / 2; - const minX = dotRadius; - const minY = dotRadius; - const maxX = Math.max(minX, this.canvas.width - dotRadius); - const maxY = Math.max(minY, this.canvas.height - dotRadius); - - if (dot.x <= minX && dot.vx < 0) { - dot.x = minX + (minX - dot.x); - dot.vx = Math.abs(dot.vx); - dot.baseVx = Math.abs(dot.baseVx); - } else if (dot.x >= maxX && dot.vx > 0) { - dot.x = maxX - (dot.x - maxX); - dot.vx = -Math.abs(dot.vx); - dot.baseVx = -Math.abs(dot.baseVx); - } - - if (dot.y <= minY && dot.vy < 0) { - dot.y = minY + (minY - dot.y); - dot.vy = Math.abs(dot.vy); - dot.baseVy = Math.abs(dot.baseVy); - } else if (dot.y >= maxY && dot.vy > 0) { - dot.y = maxY - (dot.y - maxY); - dot.vy = -Math.abs(dot.vy); - dot.baseVy = -Math.abs(dot.baseVy); - } - - // A large pointer impulse can overshoot both edges by more than - // one frame, so clamp after reflecting the position. - dot.x = Math.max(minX, Math.min(maxX, dot.x)); - dot.y = Math.max(minY, Math.min(maxY, dot.y)); - }); - } - - drawDots() { - this.dots.forEach(dot => { - const isConnected = dot.connectionCount > 0; - this.ctx.beginPath(); - this.ctx.arc(dot.x, dot.y, dot.size / 2, 0, Math.PI * 2); - this.ctx.fillStyle = isConnected ? this.config.colors.dotConnected : this.config.colors.dotDefault; - this.ctx.globalAlpha = dot.opacity; - this.ctx.shadowBlur = 6; - this.ctx.shadowColor = isConnected ? this.config.colors.dotConnectedGlow : this.config.colors.dotDefaultGlow; - this.ctx.fill(); - }); - this.ctx.globalAlpha = 1; - this.ctx.shadowBlur = 0; - } - - createConnection(dot1, dot2) { - if (this.connections.length >= this.config.maxConnections) return; - - dot1.connectionCount++; - dot2.connectionCount++; - - this.connections.push({ - dot1: dot1, - dot2: dot2, - startTime: Date.now(), - duration: Math.random() * (this.config.connectionDuration.max - this.config.connectionDuration.min) + this.config.connectionDuration.min - }); - } - - updateConnections() { - this.connections = this.connections.filter(conn => { - const elapsed = Date.now() - conn.startTime; - if (elapsed > conn.duration) { - conn.dot1.connectionCount--; - conn.dot2.connectionCount--; - return false; - } - return true; - }); - } - - drawConnections() { - this.connections.forEach(conn => { - const { dot1, dot2, startTime, duration } = conn; - const elapsed = Date.now() - startTime; - const opacity = Math.min(1, elapsed / 500); - - const gradient = this.ctx.createLinearGradient(dot1.x, dot1.y, dot2.x, dot2.y); - gradient.addColorStop(0, this.config.colors.connection.start); - gradient.addColorStop(0.5, this.config.colors.connection.middle); - gradient.addColorStop(1, this.config.colors.connection.end); - - this.ctx.beginPath(); - this.ctx.moveTo(dot1.x, dot1.y); - this.ctx.lineTo(dot2.x, dot2.y); - - this.ctx.strokeStyle = gradient; - this.ctx.lineWidth = 2; - this.ctx.globalAlpha = opacity; - - const pulse = (Math.sin((elapsed / 2000) * Math.PI * 2) + 1) / 2; - this.ctx.shadowBlur = 4 + pulse * 6; - this.ctx.shadowColor = 'rgba(118, 75, 162, 0.5)'; - - this.ctx.stroke(); - }); - this.ctx.globalAlpha = 1; - this.ctx.shadowBlur = 0; - } - - tryCreateConnections() { - for (let i = 0; i < this.dots.length; i++) { - for (let j = i + 1; j < this.dots.length; j++) { - if (Math.random() < this.config.connectionChance) { - const dot1 = this.dots[i]; - const dot2 = this.dots[j]; - - const dx = dot2.x - dot1.x; - const dy = dot2.y - dot1.y; - const distance = Math.sqrt(dx * dx + dy * dy); - - if (distance < 150 && distance > 30) { - const alreadyConnected = this.connections.some(conn => - (conn.dot1 === dot1 && conn.dot2 === dot2) || - (conn.dot1 === dot2 && conn.dot2 === dot1) - ); - if (!alreadyConnected) { - this.createConnection(dot1, dot2); - } - } - } - } - } - } - - animate() { - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - - if (!this.prefersReducedMotion) { - this.updateDots(); - this.updateConnections(); - this.tryCreateConnections(); - } - - this.drawConnections(); - this.drawDots(); - - this.rafId = requestAnimationFrame(() => this.animate()); - } -} - -document.addEventListener('DOMContentLoaded', () => { - new MatrixBackground(); -});