Star Disintegration
Home
Snippets
Star Disintegration
HTML
CSS
JS
<canvas id="c"></canvas>
html,body{ height:100%; margin:0; } body{ display:grid; place-items:center; background: black; } canvas{ width: min(92vw, 720px); height: calc(min(92vw, 720px) * 0.66); border-radius: 10px; background: linear-gradient(180deg, rgba(255,255,255,0.01), transparent 40%); display:block; }
(() => { const canvas = document.getElementById('c'); const ctx = canvas.getContext('2d', { alpha: true }); function resize() { const dpr = Math.max(1, Math.min(2, window.devicePixelRatio || 1)); const cssW = Math.min(window.innerWidth * 0.92, 720); const cssH = cssW * 0.66; canvas.style.width = cssW + 'px'; canvas.style.height = cssH + 'px'; canvas.width = Math.round(cssW * dpr); canvas.height = Math.round(cssH * dpr); ctx.setTransform(dpr,0,0,dpr,0,0); } window.addEventListener('resize', resize); resize(); const state = { cx: canvas.width/2/ (window.devicePixelRatio || 1), cy: canvas.height/2/ (window.devicePixelRatio || 1), radius: Math.min(canvas.width, canvas.height) * 0.12 / (window.devicePixelRatio || 1), particles: [], exploded: false, }; function buildStarPath(x, y, r, spikes = 5, inset = 0.5){ const path = new Path2D(); let rot = Math.PI / 2 * 3; let step = Math.PI / spikes; path.moveTo(x, y - r); for(let i=0;i<spikes;i++){ const ox = x + Math.cos(rot) * r; const oy = y + Math.sin(rot) * r; path.lineTo(ox, oy); rot += step; const ir = r * inset; const ix = x + Math.cos(rot) * ir; const iy = y + Math.sin(rot) * ir; path.lineTo(ix, iy); rot += step; } path.closePath(); return path; } function drawStar(x,y,r, t = 0){ const spikes = 5; const inset = 0.52; ctx.save(); ctx.fillStyle = 'rgba(255,214,107,0.08)'; ctx.filter = 'blur(18px)'; ctx.fill(buildStarPath(x,y, r*1.9, spikes, inset)); ctx.filter = 'none'; const g = ctx.createLinearGradient(x - r, y - r, x + r, y + r); g.addColorStop(0, '#ffd86b'); g.addColorStop(0.6, '#ff9aa2'); g.addColorStop(1, '#ff6b9f'); ctx.fillStyle = g; ctx.shadowColor = 'rgba(255,130,120,0.25)'; ctx.shadowBlur = 18; ctx.fill(buildStarPath(x,y, r, spikes, inset)); ctx.beginPath(); ctx.fillStyle = 'rgba(255,255,255,0.85)'; ctx.globalAlpha = 0.12; ctx.arc(x, y - r*0.2, r*0.3, 0, Math.PI*2); ctx.fill(); ctx.globalAlpha = 1; ctx.restore(); for(let i=0;i<6;i++){ const a = (i/6) * Math.PI*2 + t*0.004; const px = x + Math.cos(a) * r * (1.6 + Math.sin(t*0.01 + i)*0.04); const py = y + Math.sin(a) * r * (1.6 + Math.cos(t*0.01 + i)*0.04); ctx.beginPath(); ctx.fillStyle = 'rgba(255,230,160,' + (0.6 + 0.4*Math.sin(t*0.02 + i)) + ')'; ctx.arc(px, py, Math.max(1.2, r*0.06), 0, Math.PI*2); ctx.fill(); } } class P { constructor(x,y,angle,speed, color){ this.x = x; this.y = y; this.vx = Math.cos(angle) * speed + (Math.random()-0.5)*0.6; this.vy = Math.sin(angle) * speed + (Math.random()-0.5)*0.6; this.life = 1; // 1 -> 0 this.size = 2 + Math.random()*4; this.color = color; this.spin = (Math.random()-0.5)*0.2; } update(dt){ this.vx *= 0.995; this.vy *= 0.995; this.vy += 0.0016 * dt; this.x += this.vx * (dt/16); this.y += this.vy * (dt/16); this.life -= 0.012 * (dt/16); } draw(ctx){ ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.spin); ctx.globalAlpha = Math.max(0, this.life); const grad = ctx.createRadialGradient(0,0,this.size*0.1, 0,0,this.size*1.6); grad.addColorStop(0, 'rgba(255,255,255,0.95)'); grad.addColorStop(0.2, this.color); grad.addColorStop(1, 'rgba(255,255,255,0.02)'); ctx.fillStyle = grad; ctx.beginPath(); ctx.arc(0,0,this.size,0,Math.PI*2); ctx.fill(); ctx.restore(); } } function explode(x,y){ state.particles.length = 0; const base = state.radius; const count = 160 + Math.floor(Math.random()*80); for(let i=0;i<count;i++){ const a = Math.random()*Math.PI*2; const speed = 1 + Math.random()*3.2; const shade = Math.random(); const c = shade < 0.6 ? `rgba(255,${180 + Math.floor(Math.random()*60)},${80 + Math.floor(Math.random()*120)},1)` : `rgba(${240 + Math.floor(Math.random()*10)},${120 + Math.floor(Math.random()*80)},${200 + Math.floor(Math.random()*55)},1)`; state.particles.push(new P(x + (Math.random()-0.5)*base*0.6, y + (Math.random()-0.5)*base*0.6, a, speed, c)); } state.exploded = true; pulse = 1.6; } function reset(){ state.particles.length = 0; state.exploded = false; } let last = performance.now(); let t = 0; let pulse = 0; let autoTimer = setTimeout(()=> { const {cx,cy} = computeState(); explode(cx,cy); }, 900); function computeState(){ const rect = canvas.getBoundingClientRect(); state.cx = rect.width/2; state.cy = rect.height/2; state.radius = Math.min(rect.width, rect.height) * 0.14; return { cx: state.cx, cy: state.cy, r: state.radius }; } canvas.addEventListener('click', (e) => { const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; if(!state.exploded){ explode(x,y); } else { reset(); } }); window.addEventListener('keydown', (e) => { if(e.code === 'Space'){ reset(); } }); function frame(now){ const dt = Math.min(48, now - last); last = now; t += dt; const {cx,cy,r} = computeState(); ctx.clearRect(0,0,canvas.width,canvas.height); const vg = ctx.createRadialGradient(cx, cy, r*0.5, cx, cy, Math.max(canvas.width, canvas.height)); vg.addColorStop(0, 'rgba(10,12,20,0)'); vg.addColorStop(1, 'rgba(0,0,0,0.35)'); ctx.fillStyle = vg; ctx.fillRect(0,0,canvas.width,canvas.height); if(!state.exploded){ drawStar(cx, cy, r, t); } else { const alive = state.particles.length; if(alive){ ctx.beginPath(); const glow = ctx.createRadialGradient(cx,cy,0,cx,cy,r*1.6); glow.addColorStop(0, 'rgba(255,230,130,0.35)'); glow.addColorStop(0.6, 'rgba(255,120,160,0.04)'); glow.addColorStop(1, 'rgba(0,0,0,0)'); ctx.fillStyle = glow; ctx.fillRect(cx - r*2, cy - r*2, r*4, r*4); } } for(let i = state.particles.length-1; i>=0; i--){ const p = state.particles[i]; p.update(dt); if(p.life <= 0){ state.particles.splice(i,1); } else { p.draw(ctx); } } if(pulse > 0) pulse = Math.max(0, pulse - dt*0.03); requestAnimationFrame(frame); } requestAnimationFrame(frame); window.addEventListener('load', resize); })();
Ad #1
Ad #2
Scroll to Top