diff --git a/assets/app.js b/assets/app.js index 994c0ee..5f89a93 100644 --- a/assets/app.js +++ b/assets/app.js @@ -46,116 +46,56 @@ function makeSpace() { var space = document.createElement("div"); space.className = "space"; - var w = window.innerWidth; - var h = window.innerHeight; + space.setAttribute("aria-hidden", "true"); + var small = window.innerWidth < 700; + var tints = ["#ffffff", "#ffffff", "#cfe0ff", "#bcd0ff", "#ffe6c8", "#a8f0d2"]; - var starCount = w < 700 ? 80 : 150; - var stars = []; - for (var i = 0; i < starCount; i++) { - var s = document.createElement("div"); - s.className = "space__star"; - var size = 0.8 + Math.random() * 1.6; - var base = 0.2 + Math.random() * 0.55; - s.style.width = size + "px"; - s.style.height = size + "px"; - s.style.boxShadow = "0 0 " + (size * 2) + "px rgba(255,255,255," + (base * 0.5) + ")"; - space.appendChild(s); - stars.push({ - el: s, - x: Math.random() * w, - y: Math.random() * h, - vx: (Math.random() - 0.5) * 1.4, - vy: (Math.random() - 0.5) * 1.4, - base: base, - phase: Math.random() * Math.PI * 2, - twinkle: 0.3 + Math.random() * 0.5 - }); + function rnd(a, b) { return a + Math.random() * (b - a); } + + function layer(count, cls) { + var box = document.createElement("div"); + box.className = "space__layer " + cls; + for (var i = 0; i < count; i++) { + var s = document.createElement("div"); + var r = Math.random(); + var size = r < 0.7 ? rnd(1, 1.6) : r < 0.94 ? rnd(1.8, 2.4) : rnd(2.8, 3.6); + var big = r >= 0.94; + s.className = "space__star"; + s.style.left = rnd(0, 100).toFixed(2) + "%"; + s.style.top = rnd(0, 100).toFixed(2) + "%"; + s.style.width = size.toFixed(1) + "px"; + s.style.height = size.toFixed(1) + "px"; + s.style.setProperty("--c", tints[(Math.random() * tints.length) | 0]); + s.style.setProperty("--o", (big ? rnd(0.8, 1) : rnd(0.4, 0.85)).toFixed(2)); + s.style.setProperty("--g", (size * (big ? 3 : 1.5)).toFixed(1) + "px"); + s.style.setProperty("--t", rnd(3, 8).toFixed(1) + "s"); + s.style.setProperty("--d", (-rnd(0, 8)).toFixed(1) + "s"); + box.appendChild(s); + } + space.appendChild(box); } - var nebulae = [ - { x: 0.18, y: 0.30, rx: 38, ry: 22, angle: -20, c1: "120,150,255", c2: "80,200,200", strength: 0.5 }, - { x: 0.78, y: 0.22, rx: 30, ry: 44, angle: 160, c1: "180,120,255", c2: "240,150,120", strength: 0.4 }, - { x: 0.55, y: 0.78, rx: 46, ry: 26, angle: 70, c1: "150,210,255", c2: "120,240,180", strength: 0.45 } - ]; + layer(small ? 50 : 90, "space__layer--far"); + layer(small ? 22 : 40, "space__layer--near"); - nebulae.forEach(function (n, idx) { - var el = document.createElement("div"); - el.className = "space__nebula"; - el.style.width = (n.rx * 2.6) + "%"; - el.style.height = (n.ry * 2.6) + "%"; - el.style.left = (n.x * 100 - n.rx * 1.3) + "%"; - el.style.top = (n.y * 100 - n.ry * 1.3) + "%"; - el.style.background = - "radial-gradient(ellipse at center, rgba(" + n.c1 + "," + (0.22 * n.strength) + - "), rgba(" + n.c2 + "," + (0.16 * n.strength) + "), transparent 70%)"; - n.el = el; - n.seed = idx * 19.1; - n.amp = 1.6; - space.appendChild(el); - }); + if (!reduce) { + var aim = function (m) { + m.style.left = rnd(35, 105).toFixed(1) + "%"; + m.style.top = rnd(-8, 45).toFixed(1) + "%"; + }; + for (var k = 0; k < (small ? 2 : 3); k++) { + var m = document.createElement("div"); + m.className = "space__meteor"; + m.style.setProperty("--t", rnd(9, 17).toFixed(1) + "s"); + m.style.setProperty("--d", rnd(1, 9).toFixed(1) + "s"); + m.style.setProperty("--a", rnd(138, 152).toFixed(0) + "deg"); + aim(m); + m.addEventListener("animationiteration", function (e) { aim(e.target); }); + space.appendChild(m); + } + } document.body.appendChild(space); - - function place(star) { - star.el.style.transform = - "translate3d(" + star.x.toFixed(1) + "px," + star.y.toFixed(1) + "px,0)"; - } - - function placeNebula(n, t) { - var dx = Math.sin(t * 0.25 + n.seed) * n.amp; - var dy = Math.cos(t * 0.20 + n.seed * 0.7) * n.amp; - n.el.style.transform = - "translate3d(" + dx.toFixed(2) + "vw," + dy.toFixed(2) + "vh,0) rotate(" + n.angle + "deg)"; - n.el.style.opacity = 0.09 + 0.03 * Math.sin(t * 0.4 + n.seed); - } - - if (reduce) { - for (var k = 0; k < stars.length; k++) { - place(stars[k]); - stars[k].el.style.opacity = stars[k].base; - } - nebulae.forEach(function (n) { placeNebula(n, 0); }); - window.addEventListener("resize", function () { - w = window.innerWidth; - h = window.innerHeight; - for (var k = 0; k < stars.length; k++) { - if (stars[k].x > w) stars[k].x = Math.random() * w; - if (stars[k].y > h) stars[k].y = Math.random() * h; - place(stars[k]); - } - }, { passive: true }); - return; - } - - var last = performance.now(); - function frame(now) { - var dt = Math.min((now - last) / 1000, 0.1); - last = now; - var t = now * 0.00005; - - for (var k = 0; k < stars.length; k++) { - var st = stars[k]; - st.x += st.vx * dt; - st.y += st.vy * dt; - if (st.x < -4) st.x += w + 8; - else if (st.x > w + 4) st.x -= w + 8; - if (st.y < -4) st.y += h + 8; - else if (st.y > h + 4) st.y -= h + 8; - place(st); - st.el.style.opacity = - st.base * (0.65 + 0.35 * Math.sin(t * st.twinkle * 12 + st.phase)); - } - - for (var j = 0; j < nebulae.length; j++) placeNebula(nebulae[j], t); - - requestAnimationFrame(frame); - } - requestAnimationFrame(frame); - - window.addEventListener("resize", function () { - w = window.innerWidth; - h = window.innerHeight; - }, { passive: true }); } try { diff --git a/assets/style.css b/assets/style.css index 96f29b3..c66ec4f 100644 --- a/assets/style.css +++ b/assets/style.css @@ -72,30 +72,118 @@ body { .space { position: fixed; - inset: -5% -5% -5% -5%; + inset: 0; z-index: -1; overflow: hidden; pointer-events: none; + background: + radial-gradient(55% 45% at 85% 6%, rgba(111, 227, 176, 0.07), transparent 70%), + radial-gradient(60% 50% at 8% 92%, rgba(110, 130, 255, 0.08), transparent 70%); +} + +.space::before, +.space::after { + content: ""; + position: absolute; + border-radius: 50%; + animation: nebula 50s ease-in-out infinite alternate; will-change: transform; } +.space::before { + top: -14vmax; + left: -18vmax; + width: 75vmax; + height: 55vmax; + background: radial-gradient(closest-side, rgba(120, 140, 255, 0.17), rgba(80, 200, 210, 0.07) 55%, transparent); +} + +.space::after { + right: -22vmax; + bottom: -26vmax; + width: 70vmax; + height: 65vmax; + background: radial-gradient(closest-side, rgba(180, 120, 255, 0.15), rgba(240, 150, 120, 0.06) 55%, transparent); + animation-duration: 64s; + animation-direction: alternate-reverse; +} + +.space__layer { + position: absolute; + inset: -4%; + will-change: transform; + animation: drift var(--dur, 240s) linear infinite alternate; +} + +.space__layer--far { + --dur: 300s; + --dx: -1.5%; + --dy: 2%; +} + +.space__layer--near { + --dur: 180s; + --dx: -3%; + --dy: 3.5%; +} + .space__star { position: absolute; - left: 0; - top: 0; border-radius: 50%; - background: #ffffff; + background: var(--c, #fff); + box-shadow: 0 0 var(--g, 3px) var(--c, #fff); + opacity: var(--o, 0.6); + animation: twinkle var(--t, 5s) ease-in-out var(--d, 0s) infinite; } -.space__nebula { +.space__meteor { position: absolute; - border-radius: 50%; - filter: blur(80px); - will-change: transform, opacity; + width: clamp(90px, 12vmax, 170px); + height: 1.5px; + border-radius: 2px; + background: linear-gradient(90deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.95)); + box-shadow: 0 0 6px rgba(190, 215, 255, 0.5); + opacity: 0; + animation: meteor var(--t, 12s) linear var(--d, 4s) infinite; +} + +@keyframes nebula { + from { transform: translate3d(0, 0, 0) scale(1); } + to { transform: translate3d(4vw, 3vh, 0) scale(1.1); } +} + +@keyframes drift { + to { transform: translate3d(var(--dx, -2%), var(--dy, 3%), 0); } +} + +@keyframes twinkle { + 0%, 100% { opacity: var(--o, 0.6); } + 50% { opacity: calc(var(--o, 0.6) * 0.3); } +} + +@keyframes meteor { + 0% { + opacity: 0; + transform: rotate(var(--a, 145deg)) translateX(0); + } + 1.5% { + opacity: 0.9; + } + 7%, 100% { + opacity: 0; + transform: rotate(var(--a, 145deg)) translateX(55vmax); + } } @media (prefers-reduced-motion: reduce) { - .space { + .space::before, + .space::after, + .space__layer, + .space__star { + animation: none; + } + + .space__meteor { display: none; } }