Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 61 additions & 15 deletions src/controllers/galaxy_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export default class extends Controller {
this.handleResize = this.handleResize.bind(this)
window.addEventListener('resize', this.handleResize)

// Handle scroll events for performance
this.isScrolling = false
this.scrollTimeout = null
this.handleScroll = this.handleScroll.bind(this)
window.addEventListener('scroll', this.handleScroll, { passive: true })

// Handle theme changes
this.handleThemeChange = this.handleThemeChange.bind(this)
const observer = new MutationObserver(this.handleThemeChange)
Expand All @@ -27,6 +33,10 @@ export default class extends Controller {
cancelAnimationFrame(this.animationId)
}
window.removeEventListener('resize', this.handleResize)
window.removeEventListener('scroll', this.handleScroll)
if (this.scrollTimeout) {
clearTimeout(this.scrollTimeout)
}
if (this.themeObserver) {
this.themeObserver.disconnect()
}
Expand Down Expand Up @@ -62,12 +72,19 @@ export default class extends Controller {
resizeCanvas() {
if (!this.canvas) return

this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight
// Use element's client dimensions for better mobile viewport handling
const container = this.canvas.parentElement
this.canvas.width = container.clientWidth || window.innerWidth
this.canvas.height = container.clientHeight || window.innerHeight
}

createParticles() {
const particleCount = Math.floor((this.canvas.width * this.canvas.height) / 8000)
// Reduce particle count on mobile devices for better performance
const isMobile = window.innerWidth <= 768
const baseArea = this.canvas.width * this.canvas.height
const divisor = isMobile ? 12000 : 8000
const particleCount = Math.floor(baseArea / divisor)

this.particles = []

for (let i = 0; i < particleCount; i++) {
Expand All @@ -79,7 +96,7 @@ export default class extends Controller {
const isDark = document.documentElement.classList.contains('dark')
const colors = isDark
? ['#ffffff', '#f8fafc', '#e2e8f0', '#cbd5e1', '#fbbf24', '#fde047']
: ['#1e293b', '#334155', '#475569', '#64748b', '#0f172a', '#1e40af']
: ['#7c3aed', '#ec4899', '#f59e0b', '#ef4444', '#8b5cf6', '#d946ef']

// Random distribution across the sky
const x = Math.random() * this.canvas.width
Expand All @@ -99,17 +116,17 @@ export default class extends Controller {
if (starType < 0.1) {
// Bright stars (10%)
baseRadius = brightness * 2.5 + 1.5
baseOpacity = isDark ? 0.9 : 0.8
baseOpacity = isDark ? 0.9 : 1.0
twinkleSpeed = 0.03
} else if (starType < 0.3) {
// Medium stars (20%)
baseRadius = brightness * 1.5 + 0.8
baseOpacity = isDark ? 0.7 : 0.6
baseOpacity = isDark ? 0.7 : 1.0
twinkleSpeed = 0.02
} else {
// Dim stars (70%)
baseRadius = brightness * 0.8 + 0.3
baseOpacity = isDark ? 0.4 : 0.3
baseOpacity = isDark ? 0.4 : 0.9
twinkleSpeed = 0.015
}

Expand All @@ -121,8 +138,8 @@ export default class extends Controller {
radius: baseRadius,
baseRadius: baseRadius,
color: colors[Math.floor(Math.random() * colors.length)],
opacity: baseOpacity * (0.7 + Math.random() * 0.3),
baseOpacity: baseOpacity * (0.7 + Math.random() * 0.3),
opacity: isDark ? baseOpacity * (0.7 + Math.random() * 0.3) : 1.0,
baseOpacity: isDark ? baseOpacity * (0.7 + Math.random() * 0.3) : 1.0,
twinkleSpeed: twinkleSpeed + (Math.random() - 0.5) * 0.01,
twinklePhase: Math.random() * Math.PI * 2,
brightness: brightness,
Expand All @@ -142,7 +159,7 @@ export default class extends Controller {
updateParticles() {
this.time += 0.01

this.particles.forEach((particle, index) => {
this.particles.forEach((particle) => {
// Gentle drifting motion
particle.x += particle.vx
particle.y += particle.vy
Expand All @@ -154,7 +171,8 @@ export default class extends Controller {
// Twinkling effect
particle.twinklePhase += particle.twinkleSpeed
const twinkleFactor = Math.sin(particle.twinklePhase) * 0.3 + 0.7
particle.currentOpacity = particle.baseOpacity * twinkleFactor
const isDark = document.documentElement.classList.contains('dark')
particle.currentOpacity = isDark ? particle.baseOpacity * twinkleFactor : 1.0

// Size twinkling for brighter stars
if (particle.starType < 0.1) {
Expand Down Expand Up @@ -193,14 +211,21 @@ export default class extends Controller {
this.ctx.save()

// Create glow effect for bright stars
const isDark = document.documentElement.classList.contains('dark')
if (particle.starType < 0.1) {
this.ctx.shadowColor = particle.color
this.ctx.shadowBlur = particle.radius * 4
this.ctx.shadowBlur = isDark ? particle.radius * 4 : particle.radius * 6
this.ctx.shadowOffsetX = 0
this.ctx.shadowOffsetY = 0
} else if (particle.starType < 0.3) {
this.ctx.shadowColor = particle.color
this.ctx.shadowBlur = particle.radius * 2
this.ctx.shadowBlur = isDark ? particle.radius * 2 : particle.radius * 4
this.ctx.shadowOffsetX = 0
this.ctx.shadowOffsetY = 0
} else if (!isDark) {
// Add glow to all particles in light mode for visibility
this.ctx.shadowColor = particle.color
this.ctx.shadowBlur = particle.radius * 3
this.ctx.shadowOffsetX = 0
this.ctx.shadowOffsetY = 0
}
Expand Down Expand Up @@ -276,6 +301,12 @@ export default class extends Controller {
}

animate() {
// Skip animation during scroll for better performance
if (this.isScrolling) {
this.animationId = requestAnimationFrame(() => this.animate())
return
}

this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)

this.updateParticles()
Expand All @@ -290,10 +321,11 @@ export default class extends Controller {
const isDark = document.documentElement.classList.contains('dark')
const colors = isDark
? ['#ffffff', '#a855f7', '#ec4899', '#3b82f6', '#10b981']
: ['#1f2937', '#7c3aed', '#be185d', '#1d4ed8', '#047857']
: ['#7c3aed', '#ec4899', '#f59e0b', '#ef4444', '#8b5cf6']

particle.color = colors[Math.floor(Math.random() * colors.length)]
particle.opacity = isDark ? Math.random() * 0.8 + 0.2 : Math.random() * 0.9 + 0.4
particle.opacity = isDark ? Math.random() * 0.8 + 0.2 : 1.0
particle.baseOpacity = isDark ? Math.random() * 0.8 + 0.2 : 1.0
})
}

Expand All @@ -302,6 +334,20 @@ export default class extends Controller {
this.createParticles()
}

handleScroll() {
this.isScrolling = true

// Clear previous timeout
if (this.scrollTimeout) {
clearTimeout(this.scrollTimeout)
}

// Resume animation after scroll ends
this.scrollTimeout = setTimeout(() => {
this.isScrolling = false
}, 150)
}

handleThemeChange() {
// Update particle colors when theme changes
this.updateParticleColors()
Expand Down
4 changes: 2 additions & 2 deletions src/layouts/main.astro
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const navLinks = [
---

<!DOCTYPE html>
<html lang="en">
<html lang="en" class="overflow-x-hidden">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
Expand All @@ -44,7 +44,7 @@ const navLinks = [
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
</head>
<body>
<body class="overflow-x-hidden">
{showNavbar && <Navbar githubUrl={githubUrl} links={navLinks} />}

<main class={showNavbar ? "main-content with-navbar" : "main-content"}>
Expand Down
16 changes: 8 additions & 8 deletions src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Lux # For Neovim`;

<!-- Gradient overlay for better text contrast -->
<div
class="absolute inset-0 z-5 bg-gradient-to-b from-purple-50/20 via-purple-100/30 to-purple-200/40 dark:from-transparent dark:via-black/20 dark:to-black/40"
class="absolute inset-0 z-5 bg-gradient-to-b from-sky-100/50 via-blue-100/60 to-cyan-100/70 dark:from-transparent dark:via-black/20 dark:to-black/40"
>
</div>

Expand All @@ -42,7 +42,7 @@ Lux # For Neovim`;
<!-- Status Badge -->
<div class="mt-20">
<span
class="inline-flex items-center px-4 py-2 rounded-full text-sm font-medium bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300"
class="inline-flex items-center px-4 py-2 rounded-full text-sm font-medium bg-amber-100 text-amber-800 dark:bg-orange-900/30 dark:text-orange-300"
>
<span class="w-2 h-2 bg-orange-500 rounded-full mr-2 animate-pulse"
></span>
Expand Down Expand Up @@ -79,7 +79,7 @@ Lux # For Neovim`;
<div class="flex flex-col sm:flex-row gap-4 mb-12">
<a
href="#installation"
class="inline-flex items-center px-8 py-4 text-lg font-semibold text-white bg-gradient-to-r from-purple-600 to-pink-600 rounded-xl hover:from-purple-700 hover:to-pink-700 transition-all duration-300 hover:scale-105 shadow-lg hover:shadow-purple-500/25"
class="inline-flex items-center px-8 py-4 text-lg font-semibold text-white bg-gradient-to-r from-purple-400 to-pink-400 dark:from-purple-600 dark:to-pink-600 rounded-xl hover:from-purple-500 hover:to-pink-500 dark:hover:from-purple-700 dark:hover:to-pink-700 transition-all duration-300 hover:scale-105 shadow-lg hover:shadow-purple-500/25"
>
<svg
class="w-5 h-5 mr-2"
Expand Down Expand Up @@ -206,7 +206,7 @@ Lux # For Neovim`;
<!-- Features Section -->
<section
class="py-16 md:py-20
bg-white dark:bg-slate-900
bg-slate-50 dark:bg-slate-900
dark:from-slate-800 dark:via-slate-700 dark:to-slate-600
dark:backdrop-blur-sm"
>
Expand All @@ -226,7 +226,7 @@ Lux # For Neovim`;
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<!-- Custom Dashboard -->
<div
class="bg-white dark:bg-slate-800 rounded-2xl p-8 shadow-lg hover:shadow-xl transition-shadow duration-300"
class="bg-white/30 dark:bg-slate-800 rounded-2xl p-8 shadow-lg hover:shadow-xl transition-shadow duration-300"
>
<div
class="w-16 h-16 bg-gradient-to-br from-blue-500 to-cyan-500 rounded-xl flex items-center justify-center mb-6"
Expand Down Expand Up @@ -598,7 +598,7 @@ Lux # For Neovim`;
<span class="font-medium">Vim or Neovim (recent versions)</span>
</li>
<li
class="flex items-center text-slate-700 dark:text-slate-300 p-3 bg-slate-100 dark:bg-slate-700/30 rounded-lg"
class="flex items-center text-slate-700 dark:text-slate-300 p-3 bg-white/50 dark:bg-slate-700/30 rounded-lg"
>
<div
class="w-8 h-8 bg-green-100 dark:bg-green-900/30 rounded-full flex items-center justify-center mr-4"
Expand All @@ -621,7 +621,7 @@ Lux # For Neovim`;
</ul>

<div
class="bg-slate-100 dark:bg-slate-800/50 rounded-xl p-6 border border-blue-100 dark:border-slate-600"
class="bg-slate-100 dark:bg-slate-800/50 rounded-xl p-6 border border-purple-100 dark:border-slate-600"
>
<div class="flex items-center mb-4">
<div
Expand Down Expand Up @@ -681,7 +681,7 @@ Lux # For Neovim`;

<!-- Gradient overlay for better text contrast -->
<div
class="absolute inset-0 z-5 bg-gradient-to-b from-transparent via-black/5 to-black/15 dark:from-transparent dark:via-black/20 dark:to-black/40"
class="absolute inset-0 z-5 bg-gradient-to-b from-sky-100/50 via-blue-100/60 to-cyan-100/70 dark:from-transparent dark:via-black/20 dark:to-black/40"
>
</div>

Expand Down
8 changes: 6 additions & 2 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@

/* Galaxy background styles */
#galaxy-background, #galaxy-background-cta {
background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 25%, #a5b4fc 50%, #8b5cf6 75%, #7c3aed 100%);
background: linear-gradient(135deg, #f3e8ff 0%, #e9d5ff 15%, #d8b4fe 35%, #c084fc 55%, #a855f7 75%, #7c3aed 100%);
transition: background 0.3s ease;
will-change: scroll-position;
contain: layout style paint;
}

.dark #galaxy-background, .dark #galaxy-background-cta {
background: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 50%, #16213e 100%);
}

/* Canvas styling */
#galaxy-canvas {
#galaxy-canvas, #galaxy-canvas-cta {
display: block;
width: 100%;
height: 100%;
cursor: crosshair;
will-change: transform;
transform: translateZ(0);
}

/* Smooth transitions for theme changes */
Expand Down