FeedGine launch
This commit is contained in:
531
scripts/hex-background.js
Normal file
531
scripts/hex-background.js
Normal file
@@ -0,0 +1,531 @@
|
||||
/**
|
||||
* Space Background — Glass Shards · Animated Nebula · Drifting Stars · Shooting Stars
|
||||
* No hex grid. Cursor scatters floating glass fragments.
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
let canvas, ctx;
|
||||
let W = 0, H = 0;
|
||||
let mouse = { x: -2000, y: -2000 };
|
||||
let rafId;
|
||||
let initialized = false;
|
||||
let time = 0;
|
||||
|
||||
/* ──────────────────────────────────────────────
|
||||
NEBULA BLOBS (animated radial gradients) — dimmed
|
||||
────────────────────────────────────────────── */
|
||||
const NEBULAS = [
|
||||
{ px: 0.12, py: 0.25, pr: 0.50, cr: 255, cg: 80, cb: 0, a: 0.040, spx: 0.00014, spy: 0.00007, phase: 0.0 },
|
||||
{ px: 0.82, py: 0.55, pr: 0.55, cr: 0, cg: 255, cb: 136, a: 0.028, spx:-0.00009, spy: 0.00011, phase: 2.1 },
|
||||
{ px: 0.50, py: 0.88, pr: 0.42, cr: 0, cg: 180, cb: 255, a: 0.022, spx: 0.00007, spy:-0.00009, phase: 4.3 },
|
||||
{ px: 0.72, py: 0.12, pr: 0.38, cr: 160, cg: 0, cb: 255, a: 0.016, spx:-0.00010, spy: 0.00005, phase: 1.5 },
|
||||
{ px: 0.35, py: 0.65, pr: 0.32, cr: 255, cg: 140, cb: 0, a: 0.016, spx: 0.00005, spy:-0.00006, phase: 3.7 },
|
||||
];
|
||||
|
||||
function drawNebulas() {
|
||||
for (let i = 0; i < NEBULAS.length; i++) {
|
||||
const n = NEBULAS[i];
|
||||
const ox = Math.sin(time * n.spx * 800 + n.phase) * W * 0.10;
|
||||
const oy = Math.cos(time * n.spy * 800 + n.phase + 1.2) * H * 0.10;
|
||||
const cx = n.px * W + ox;
|
||||
const cy = n.py * H + oy;
|
||||
const rad = n.pr * Math.min(W, H);
|
||||
const pa = n.a * (0.65 + 0.35 * Math.sin(time * 0.28 + n.phase));
|
||||
|
||||
const g = ctx.createRadialGradient(cx, cy, 0, cx, cy, rad);
|
||||
g.addColorStop(0, `rgba(${n.cr},${n.cg},${n.cb},${pa})`);
|
||||
g.addColorStop(0.45,`rgba(${n.cr},${n.cg},${n.cb},${pa * 0.35})`);
|
||||
g.addColorStop(1, `rgba(${n.cr},${n.cg},${n.cb},0)`);
|
||||
ctx.fillStyle = g;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, rad, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────
|
||||
DRIFTING STARS — dimmed halos
|
||||
────────────────────────────────────────────── */
|
||||
const STAR_COUNT = 220;
|
||||
let stars = [];
|
||||
|
||||
function initStars() {
|
||||
stars = [];
|
||||
for (let i = 0; i < STAR_COUNT; i++) {
|
||||
stars.push({
|
||||
x: Math.random() * W,
|
||||
y: Math.random() * H,
|
||||
r: 0.35 + Math.random() * 1.65,
|
||||
vx: (Math.random() - 0.5) * 0.055,
|
||||
vy: 0.018 + Math.random() * 0.055,
|
||||
phase: Math.random() * Math.PI * 2,
|
||||
twinkleSpeed: 0.4 + Math.random() * 1.6,
|
||||
// 0=white 1=neon-green 2=neon-teal
|
||||
type: Math.random() < 0.78 ? 0 : (Math.random() < 0.5 ? 1 : 2),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function drawStars() {
|
||||
for (let i = 0; i < stars.length; i++) {
|
||||
const s = stars[i];
|
||||
const tw = 0.25 + 0.75 * (0.5 + 0.5 * Math.sin(time * s.twinkleSpeed + s.phase));
|
||||
|
||||
s.x += s.vx;
|
||||
s.y += s.vy;
|
||||
if (s.y > H + 2) { s.y = -2; s.x = Math.random() * W; }
|
||||
if (s.x < -2) s.x = W + 2;
|
||||
if (s.x > W + 2) s.x = -2;
|
||||
|
||||
const clr = s.type === 0
|
||||
? `rgba(255,255,255,${tw * 0.70})`
|
||||
: s.type === 1
|
||||
? `rgba(0,255,136,${tw * 0.50})`
|
||||
: `rgba(0,220,255,${tw * 0.50})`;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
|
||||
ctx.fillStyle = clr;
|
||||
ctx.fill();
|
||||
|
||||
// Halo only on larger stars, dimmed
|
||||
if (s.r > 1.1) {
|
||||
const halo = ctx.createRadialGradient(s.x, s.y, 0, s.x, s.y, s.r * 3.5);
|
||||
const ha = tw * (s.type === 0 ? 0.14 : 0.10);
|
||||
halo.addColorStop(0, s.type === 0
|
||||
? `rgba(255,255,255,${ha})`
|
||||
: s.type === 1
|
||||
? `rgba(0,255,136,${ha})`
|
||||
: `rgba(0,220,255,${ha})`);
|
||||
halo.addColorStop(1, 'rgba(0,0,0,0)');
|
||||
ctx.beginPath();
|
||||
ctx.arc(s.x, s.y, s.r * 3.5, 0, Math.PI * 2);
|
||||
ctx.fillStyle = halo;
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────
|
||||
SHOOTING STARS
|
||||
────────────────────────────────────────────── */
|
||||
const MAX_SHOOTING_STARS = 3;
|
||||
let shootingStars = [];
|
||||
let nextShootingStarAt = 0; // time value when next star spawns
|
||||
|
||||
function spawnShootingStar() {
|
||||
// Spawn from top or right edge, travel down-left or down-right
|
||||
const fromRight = Math.random() < 0.5;
|
||||
const startX = fromRight ? W * (0.5 + Math.random() * 0.6) : W * Math.random() * 0.7;
|
||||
const startY = Math.random() * H * 0.45;
|
||||
const angle = (Math.PI / 4) + (Math.random() - 0.5) * 0.6; // ~45° downward
|
||||
const speed = 6 + Math.random() * 9;
|
||||
const length = 80 + Math.random() * 160;
|
||||
|
||||
shootingStars.push({
|
||||
x: startX,
|
||||
y: startY,
|
||||
vx: Math.cos(angle) * speed * (fromRight ? -1 : 1),
|
||||
vy: Math.sin(angle) * speed,
|
||||
length,
|
||||
alpha: 0,
|
||||
fadeIn: true,
|
||||
life: 0,
|
||||
maxLife: (length / speed) * 1.6, // frames to live
|
||||
});
|
||||
}
|
||||
|
||||
function updateDrawShootingStars() {
|
||||
// Possibly spawn a new one
|
||||
if (shootingStars.length < MAX_SHOOTING_STARS && time > nextShootingStarAt) {
|
||||
spawnShootingStar();
|
||||
// Next star between 4–14 seconds of time units (time += 0.016/frame)
|
||||
nextShootingStarAt = time + 4 + Math.random() * 10;
|
||||
}
|
||||
|
||||
for (let i = shootingStars.length - 1; i >= 0; i--) {
|
||||
const s = shootingStars[i];
|
||||
s.life++;
|
||||
|
||||
// Fade in quickly, fade out near end
|
||||
if (s.life < 8) {
|
||||
s.alpha = s.life / 8;
|
||||
} else if (s.life > s.maxLife - 10) {
|
||||
s.alpha = Math.max(0, (s.maxLife - s.life) / 10);
|
||||
} else {
|
||||
s.alpha = 1;
|
||||
}
|
||||
|
||||
s.x += s.vx;
|
||||
s.y += s.vy;
|
||||
|
||||
// Draw trail
|
||||
const tailX = s.x - (s.vx / Math.hypot(s.vx, s.vy)) * s.length;
|
||||
const tailY = s.y - (s.vy / Math.hypot(s.vx, s.vy)) * s.length;
|
||||
|
||||
const grad = ctx.createLinearGradient(s.x, s.y, tailX, tailY);
|
||||
grad.addColorStop(0, `rgba(255,255,255,${s.alpha * 0.90})`);
|
||||
grad.addColorStop(0.15,`rgba(220,235,255,${s.alpha * 0.55})`);
|
||||
grad.addColorStop(1, `rgba(180,210,255,0)`);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(s.x, s.y);
|
||||
ctx.lineTo(tailX, tailY);
|
||||
ctx.strokeStyle = grad;
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.stroke();
|
||||
|
||||
// Bright head dot
|
||||
ctx.beginPath();
|
||||
ctx.arc(s.x, s.y, 1.4, 0, Math.PI * 2);
|
||||
ctx.fillStyle = `rgba(255,255,255,${s.alpha * 0.95})`;
|
||||
ctx.fill();
|
||||
|
||||
// Remove if expired or off-screen
|
||||
if (s.life >= s.maxLife || s.x < -50 || s.x > W + 50 || s.y > H + 50) {
|
||||
shootingStars.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────
|
||||
GLASS SHARDS — fixed physics
|
||||
────────────────────────────────────────────── */
|
||||
const SHARD_COUNT = 30;
|
||||
const SHARD_RADIUS = 230; // mouse influence radius
|
||||
const SCATTER_FORCE = 3.0;
|
||||
const ROT_V_MAX = 0.018; // cap on rotation speed
|
||||
|
||||
const SHARD_COLORS = [
|
||||
{ r: 255, g: 102, b: 0 }, // neon orange
|
||||
{ r: 0, g: 255, b: 136 }, // neon green
|
||||
{ r: 0, g: 220, b: 255 }, // neon teal
|
||||
{ r: 255, g: 255, b: 255 }, // white
|
||||
{ r: 255, g: 180, b: 0 }, // amber
|
||||
];
|
||||
|
||||
let shards = [];
|
||||
|
||||
function buildVerts(size, sides) {
|
||||
const verts = [];
|
||||
const base = Math.random() * Math.PI * 2;
|
||||
for (let i = 0; i < sides; i++) {
|
||||
const a = base + (Math.PI * 2 * i / sides) + (Math.random() - 0.5) * 0.65;
|
||||
const r = size * (0.55 + Math.random() * 0.45);
|
||||
verts.push([Math.cos(a) * r, Math.sin(a) * r]);
|
||||
}
|
||||
return verts;
|
||||
}
|
||||
|
||||
function initShards() {
|
||||
shards = [];
|
||||
for (let i = 0; i < SHARD_COUNT; i++) {
|
||||
const size = 11 + Math.random() * 42;
|
||||
const sides = 3 + Math.floor(Math.random() * 3); // 3–5 sides
|
||||
const col = SHARD_COLORS[Math.floor(Math.random() * SHARD_COLORS.length)];
|
||||
shards.push({
|
||||
x: Math.random() * W,
|
||||
y: Math.random() * H,
|
||||
vx: (Math.random() - 0.5) * 0.22,
|
||||
vy: (Math.random() - 0.5) * 0.22,
|
||||
rot: Math.random() * Math.PI * 2,
|
||||
rotV: (Math.random() - 0.5) * 0.003, // very gentle initial spin
|
||||
verts: buildVerts(size, sides),
|
||||
size, col,
|
||||
alpha: 0.18 + Math.random() * 0.24,
|
||||
alphaTarget: 0.18 + Math.random() * 0.24,
|
||||
svx: 0, svy: 0,
|
||||
phase: Math.random() * Math.PI * 2,
|
||||
floatSpeed: 0.20 + Math.random() * 0.45, // slower float
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function drawShard(s) {
|
||||
ctx.save();
|
||||
ctx.translate(s.x, s.y);
|
||||
ctx.rotate(s.rot);
|
||||
|
||||
const { r, g, b } = s.col;
|
||||
const al = s.alpha;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(s.verts[0][0], s.verts[0][1]);
|
||||
for (let i = 1; i < s.verts.length; i++) ctx.lineTo(s.verts[i][0], s.verts[i][1]);
|
||||
ctx.closePath();
|
||||
|
||||
// Glass fill
|
||||
const fill = ctx.createLinearGradient(-s.size, -s.size, s.size * 0.6, s.size * 0.6);
|
||||
fill.addColorStop(0, `rgba(${r},${g},${b},${al * 0.18})`);
|
||||
fill.addColorStop(0.45,`rgba(255,255,255,${al * 0.08})`);
|
||||
fill.addColorStop(1, `rgba(${r},${g},${b},${al * 0.03})`);
|
||||
ctx.fillStyle = fill;
|
||||
ctx.fill();
|
||||
|
||||
// Neon outline — dimmed
|
||||
ctx.shadowColor = `rgba(${r},${g},${b},0.45)`;
|
||||
ctx.shadowBlur = 7;
|
||||
ctx.strokeStyle = `rgba(${r},${g},${b},${al * 0.70})`;
|
||||
ctx.lineWidth = 1.0;
|
||||
ctx.stroke();
|
||||
|
||||
// Soft outer glow — dimmed
|
||||
ctx.shadowBlur = 14;
|
||||
ctx.strokeStyle = `rgba(${r},${g},${b},${al * 0.18})`;
|
||||
ctx.lineWidth = 2.0;
|
||||
ctx.stroke();
|
||||
ctx.shadowBlur = 0;
|
||||
|
||||
// Inner highlight
|
||||
if (s.verts.length >= 2) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(s.verts[0][0] * 0.55, s.verts[0][1] * 0.55);
|
||||
ctx.lineTo(s.verts[1][0] * 0.55, s.verts[1][1] * 0.55);
|
||||
ctx.strokeStyle = `rgba(255,255,255,${al * 0.40})`;
|
||||
ctx.lineWidth = 0.8;
|
||||
ctx.shadowBlur = 3;
|
||||
ctx.shadowColor = 'rgba(255,255,255,0.3)';
|
||||
ctx.stroke();
|
||||
ctx.shadowBlur = 0;
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function updateShards() {
|
||||
const infSq = SHARD_RADIUS * SHARD_RADIUS;
|
||||
|
||||
for (let i = 0; i < shards.length; i++) {
|
||||
const s = shards[i];
|
||||
|
||||
// Gentle float bob
|
||||
s.vy += Math.sin(time * s.floatSpeed + s.phase) * 0.002;
|
||||
|
||||
// Mouse scatter
|
||||
if (mouse.x > -1000) {
|
||||
const dx = s.x - mouse.x;
|
||||
const dy = s.y - mouse.y;
|
||||
const distSq = dx * dx + dy * dy;
|
||||
|
||||
if (distSq < infSq) {
|
||||
const dist = Math.sqrt(distSq);
|
||||
const force = (1 - dist / SHARD_RADIUS) * SCATTER_FORCE;
|
||||
const ang = Math.atan2(dy, dx);
|
||||
s.svx += Math.cos(ang) * force * 0.065;
|
||||
s.svy += Math.sin(ang) * force * 0.065;
|
||||
// Small nudge to rotation — NOT multiplicative
|
||||
s.rotV += (Math.random() - 0.5) * 0.004 * (1 - dist / SHARD_RADIUS);
|
||||
// Brighten on interaction
|
||||
s.alphaTarget = Math.min(0.75, s.alphaTarget + 0.03);
|
||||
}
|
||||
}
|
||||
|
||||
// Rotation damping — keeps shards from spinning endlessly
|
||||
s.rotV *= 0.96;
|
||||
// Hard cap on rotation speed
|
||||
if (s.rotV > ROT_V_MAX) s.rotV = ROT_V_MAX;
|
||||
if (s.rotV < -ROT_V_MAX) s.rotV = -ROT_V_MAX;
|
||||
|
||||
// Scatter velocity damping
|
||||
s.svx *= 0.92;
|
||||
s.svy *= 0.92;
|
||||
s.vx *= 0.998;
|
||||
s.vy *= 0.998;
|
||||
|
||||
// Apply
|
||||
s.x += s.vx + s.svx;
|
||||
s.y += s.vy + s.svy;
|
||||
s.rot += s.rotV;
|
||||
|
||||
// Fade alpha toward target
|
||||
s.alpha += (s.alphaTarget - s.alpha) * 0.025;
|
||||
// Slowly restore target
|
||||
const base = 0.18;
|
||||
if (s.alphaTarget > base) s.alphaTarget -= 0.006;
|
||||
|
||||
// Edge wrap
|
||||
const m = s.size + 10;
|
||||
if (s.x < -m) s.x = W + m;
|
||||
if (s.x > W + m) s.x = -m;
|
||||
if (s.y < -m) s.y = H + m;
|
||||
if (s.y > H + m) s.y = -m;
|
||||
}
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────
|
||||
CURSOR AURA — dimmed
|
||||
────────────────────────────────────────────── */
|
||||
function drawCursorAura() {
|
||||
const r = 130 + Math.sin(time * 1.8) * 22;
|
||||
const aura = ctx.createRadialGradient(mouse.x, mouse.y, 0, mouse.x, mouse.y, r);
|
||||
aura.addColorStop(0, 'rgba(255,102,0,0.04)');
|
||||
aura.addColorStop(0.5, 'rgba(255,102,0,0.012)');
|
||||
aura.addColorStop(1, 'rgba(255,102,0,0)');
|
||||
ctx.fillStyle = aura;
|
||||
ctx.beginPath();
|
||||
ctx.arc(mouse.x, mouse.y, r, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────
|
||||
MAIN LOOP
|
||||
────────────────────────────────────────────── */
|
||||
function animate() {
|
||||
if (!initialized) return;
|
||||
|
||||
time += 0.016;
|
||||
|
||||
ctx.clearRect(0, 0, W, H);
|
||||
|
||||
drawNebulas();
|
||||
drawStars();
|
||||
updateDrawShootingStars();
|
||||
updateShards();
|
||||
for (let i = 0; i < shards.length; i++) drawShard(shards[i]);
|
||||
if (mouse.x > -1000) drawCursorAura();
|
||||
|
||||
rafId = requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────
|
||||
MOBILE — one-shot static render
|
||||
────────────────────────────────────────────── */
|
||||
function initMobileStatic() {
|
||||
const c = document.getElementById('hexCanvas');
|
||||
if (!c) return;
|
||||
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
||||
const w = window.innerWidth;
|
||||
const h = window.innerHeight;
|
||||
c.width = w * dpr;
|
||||
c.height = h * dpr;
|
||||
c.style.width = w + 'px';
|
||||
c.style.height = h + 'px';
|
||||
const cx = c.getContext('2d');
|
||||
cx.scale(dpr, dpr);
|
||||
|
||||
// Nebula blobs — dimmed
|
||||
const g1 = cx.createRadialGradient(w*0.15, h*0.3, 0, w*0.15, h*0.3, w*0.5);
|
||||
g1.addColorStop(0, 'rgba(255,80,0,0.04)'); g1.addColorStop(1, 'rgba(255,80,0,0)');
|
||||
cx.fillStyle = g1; cx.fillRect(0, 0, w, h);
|
||||
|
||||
const g2 = cx.createRadialGradient(w*0.82, h*0.65, 0, w*0.82, h*0.65, w*0.45);
|
||||
g2.addColorStop(0, 'rgba(0,255,136,0.03)'); g2.addColorStop(1, 'rgba(0,255,136,0)');
|
||||
cx.fillStyle = g2; cx.fillRect(0, 0, w, h);
|
||||
|
||||
const g3 = cx.createRadialGradient(w*0.5, h*0.85, 0, w*0.5, h*0.85, w*0.4);
|
||||
g3.addColorStop(0, 'rgba(0,180,255,0.025)'); g3.addColorStop(1, 'rgba(0,180,255,0)');
|
||||
cx.fillStyle = g3; cx.fillRect(0, 0, w, h);
|
||||
|
||||
// Stars
|
||||
for (let i = 0; i < 130; i++) {
|
||||
const sx = Math.random() * w;
|
||||
const sy = Math.random() * h;
|
||||
const sr = 0.35 + Math.random() * 1.5;
|
||||
const sa = 0.20 + Math.random() * 0.55;
|
||||
cx.beginPath();
|
||||
cx.arc(sx, sy, sr, 0, Math.PI * 2);
|
||||
cx.fillStyle = `rgba(255,255,255,${sa})`;
|
||||
cx.fill();
|
||||
}
|
||||
|
||||
// Glass shards
|
||||
const MCOLS = [[255,102,0],[0,255,136],[0,220,255],[255,255,255],[255,180,0]];
|
||||
for (let i = 0; i < 14; i++) {
|
||||
const sx = Math.random() * w;
|
||||
const sy = Math.random() * h;
|
||||
const ss = 10 + Math.random() * 35;
|
||||
const sides = 3 + Math.floor(Math.random() * 3);
|
||||
const [cr,cg,cb] = MCOLS[Math.floor(Math.random() * MCOLS.length)];
|
||||
cx.save();
|
||||
cx.translate(sx, sy);
|
||||
cx.rotate(Math.random() * Math.PI * 2);
|
||||
cx.beginPath();
|
||||
for (let j = 0; j < sides; j++) {
|
||||
const a = (Math.PI * 2 * j / sides) + (Math.random()-0.5)*0.6;
|
||||
const r = ss * (0.55 + Math.random() * 0.45);
|
||||
j === 0 ? cx.moveTo(Math.cos(a)*r, Math.sin(a)*r)
|
||||
: cx.lineTo(Math.cos(a)*r, Math.sin(a)*r);
|
||||
}
|
||||
cx.closePath();
|
||||
cx.fillStyle = `rgba(${cr},${cg},${cb},0.04)`;
|
||||
cx.strokeStyle = `rgba(${cr},${cg},${cb},0.38)`;
|
||||
cx.lineWidth = 1;
|
||||
cx.fill();
|
||||
cx.shadowColor = `rgba(${cr},${cg},${cb},0.45)`;
|
||||
cx.shadowBlur = 6;
|
||||
cx.stroke();
|
||||
cx.shadowBlur = 0;
|
||||
cx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
/* ──────────────────────────────────────────────
|
||||
INIT
|
||||
────────────────────────────────────────────── */
|
||||
function init() {
|
||||
if (window.innerWidth <= 768 || ('ontouchstart' in window)) {
|
||||
initMobileStatic();
|
||||
return;
|
||||
}
|
||||
|
||||
canvas = document.getElementById('hexCanvas');
|
||||
if (!canvas) return;
|
||||
|
||||
ctx = canvas.getContext('2d', { alpha: true });
|
||||
resize();
|
||||
|
||||
window.addEventListener('resize', debounce(resize, 200));
|
||||
document.addEventListener('mousemove', e => {
|
||||
mouse.x = e.clientX;
|
||||
mouse.y = e.clientY;
|
||||
}, { passive: true });
|
||||
document.addEventListener('mouseleave', () => {
|
||||
mouse.x = -2000;
|
||||
mouse.y = -2000;
|
||||
});
|
||||
|
||||
// First shooting star after a short delay
|
||||
nextShootingStarAt = 3 + Math.random() * 5;
|
||||
|
||||
initialized = true;
|
||||
animate();
|
||||
}
|
||||
|
||||
function resize() {
|
||||
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
||||
W = window.innerWidth;
|
||||
H = window.innerHeight;
|
||||
canvas.width = W * dpr;
|
||||
canvas.height = H * dpr;
|
||||
canvas.style.width = W + 'px';
|
||||
canvas.style.height = H + 'px';
|
||||
canvas.style.position = 'fixed';
|
||||
canvas.style.top = '0';
|
||||
canvas.style.left = '0';
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
ctx.scale(dpr, dpr);
|
||||
initStars();
|
||||
initShards();
|
||||
}
|
||||
|
||||
function debounce(fn, ms) {
|
||||
let t;
|
||||
return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); };
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
if (rafId) cancelAnimationFrame(rafId);
|
||||
initialized = false;
|
||||
stars = []; shards = []; shootingStars = [];
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
|
||||
window.HexBackground = { init, destroy };
|
||||
})();
|
||||
Reference in New Issue
Block a user