75 lines
2.3 KiB
JavaScript
75 lines
2.3 KiB
JavaScript
// === SuperSunday moving bubbles (canvas) — robust autoload ===
|
|
(function(){
|
|
function ensureCanvas(){
|
|
let layer = document.getElementById('bubble-layer');
|
|
if (!layer) {
|
|
layer = document.createElement('canvas');
|
|
layer.id = 'bubble-layer';
|
|
document.body.appendChild(layer);
|
|
}
|
|
return layer;
|
|
}
|
|
|
|
const c = ensureCanvas();
|
|
const ctx = c.getContext('2d', { alpha: true });
|
|
|
|
function resize(){
|
|
c.width = window.innerWidth;
|
|
c.height = window.innerHeight;
|
|
}
|
|
resize();
|
|
window.addEventListener('resize', resize);
|
|
|
|
const COLORS = [
|
|
{from:'#1f2a6d', to:'#5b39a8'},
|
|
{from:'#1a255d', to:'#3e2a7a'},
|
|
{from:'#233278', to:'#7a42c4'},
|
|
];
|
|
const BUBBLES = [];
|
|
const N = Math.min(24, Math.max(12, Math.floor((window.innerWidth*window.innerHeight)/120000)));
|
|
function rand(a,b){ return a + Math.random()*(b-a); }
|
|
|
|
function makeBubble(){
|
|
const r = rand(30, 120);
|
|
const x = rand(-0.1*c.width, 1.1*c.width);
|
|
const y = rand(-0.1*c.height, 1.1*c.height);
|
|
const vy = rand(0.05, 0.2) * (Math.random()<0.5? 1 : -1);
|
|
const vx = rand(-0.08, 0.08);
|
|
const col = COLORS[Math.floor(Math.random()*COLORS.length)];
|
|
const opacity = rand(0.08, 0.22);
|
|
return {x,y,r,vx,vy,col,opacity, phase: rand(0, Math.PI*2)};
|
|
}
|
|
for(let i=0;i<N;i++) BUBBLES.push(makeBubble());
|
|
|
|
function tick(){
|
|
ctx.clearRect(0,0,c.width,c.height);
|
|
for(const b of BUBBLES){
|
|
b.phase += 0.01 + (b.r/1200);
|
|
const sway = Math.sin(b.phase) * 0.3;
|
|
b.x += b.vx + sway;
|
|
b.y += b.vy;
|
|
if (b.x < -200 || b.x > c.width+200 || b.y < -200 || b.y > c.height+200) {
|
|
const idx = BUBBLES.indexOf(b);
|
|
BUBBLES[idx] = makeBubble();
|
|
continue;
|
|
}
|
|
const g = ctx.createRadialGradient(b.x, b.y, b.r*0.1, b.x, b.y, b.r);
|
|
g.addColorStop(0, hexA(b.col.to, b.opacity*1.0));
|
|
g.addColorStop(1, hexA(b.col.from, 0));
|
|
ctx.fillStyle = g;
|
|
ctx.beginPath();
|
|
ctx.arc(b.x, b.y, b.r, 0, Math.PI*2);
|
|
ctx.fill();
|
|
}
|
|
requestAnimationFrame(tick);
|
|
}
|
|
function hexA(hex, a){
|
|
const h = hex.replace('#','');
|
|
const r = parseInt(h.substring(0,2),16);
|
|
const g = parseInt(h.substring(2,4),16);
|
|
const b = parseInt(h.substring(4,6),16);
|
|
return `rgba(${r},${g},${b},${a})`;
|
|
}
|
|
requestAnimationFrame(tick);
|
|
})();
|