Created
January 26, 2026 18:16
-
-
Save felipeabajo/71fce970468e2a10534d4b272e6cfefd to your computer and use it in GitHub Desktop.
HTML5 Canvas animation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <style> | |
| :root{ | |
| --bg1:#0f172a; | |
| --bg2:#111827; | |
| --acc1:#2563eb; | |
| --acc2:#9333ea; | |
| --acc3:#22c55e; | |
| --acc4:#f59e0b; | |
| --white:#e5e7eb; | |
| --muted:#94a3b8; | |
| } | |
| #animation-software-development{ | |
| height:15rem !important; | |
| max-height:500px !important; | |
| margin:0; | |
| background: radial-gradient(1200px 800px at 70% 20%, #1e293b 0%, var(--bg1) 55%, var(--bg2) 100%); | |
| font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; | |
| color: var(--white); | |
| } | |
| .wrap{position:relative; height:100%; width:100%; overflow:hidden} | |
| #scene{position:absolute; inset:0; display:block} | |
| </style> | |
| <div id="animation-software-development"> | |
| <div class="wrap"> | |
| <canvas id="scene" aria-hidden="true"></canvas> | |
| </div> | |
| </div> | |
| <script> | |
| const TAU = Math.PI * 2; | |
| const rand = (a,b) => a + Math.random()*(b-a); | |
| const cfg = { | |
| speed: 1, | |
| codeLines: [ | |
| "<div class=\"app\">", | |
| " <header>Power Platform</header>", | |
| " <section>Dynamics 365</section>", | |
| " <button>Iniciar flujo</button>", | |
| "</div>", | |
| "<entity name=\"Cuenta\">", | |
| " <field name=\"ClienteID\" />", | |
| "</entity>", | |
| ], | |
| colors: { | |
| tag: "#2563eb", | |
| attr: "#9333ea", | |
| val: "#22c55e", | |
| text: "#e5e7eb", | |
| accent: "#f59e0b" | |
| } | |
| }; | |
| const canvas = document.getElementById('scene'); | |
| const ctx = canvas.getContext('2d'); | |
| const DPR = Math.min(window.devicePixelRatio || 1, 2); | |
| function resize(){ | |
| const {innerWidth:w, innerHeight:h} = window; | |
| canvas.width = Math.floor(w * DPR); | |
| canvas.height = Math.floor(h * DPR); | |
| canvas.style.width = w + 'px'; | |
| canvas.style.height = h + 'px'; | |
| ctx.setTransform(DPR,0,0,DPR,0,0); | |
| } | |
| window.addEventListener('resize', resize); | |
| resize(); | |
| class CodeParticle{ | |
| constructor(){ this.reset(true); } | |
| reset(initial=false){ | |
| const w = canvas.width / DPR, h = canvas.height / DPR; | |
| this.x = rand(-w*0.1, w*1.1); | |
| this.y = initial ? rand(h*0.1, h*0.9) : (h + rand(10,200)); | |
| this.vy = -rand(.4, 1.1); | |
| this.vx = rand(-.2, .2); | |
| this.alpha = rand(.4, .95); | |
| this.scale = rand(.7, 1.6); | |
| this.text = this.pickCode(); | |
| this.spin = rand(-.004, .004); | |
| this.angle = rand(-.2, .2); | |
| } | |
| pickCode(){ | |
| const s = cfg.codeLines[Math.floor(Math.random()*cfg.codeLines.length)]; | |
| return s | |
| .replace(/(&?<!?)(\/?)([a-zA-Z!]+)(?=[\s>])/g, '{tag}<$2$3{/}') | |
| .replace(/([a-z-]+)(=)(\"[^\"]*\")/g, '{attr}$1{/}=$3') | |
| .replace(/\"([^\"]*)\"/g, '{val}="$1"{/}') | |
| } | |
| step(dt){ | |
| this.x += this.vx * dt * cfg.speed; | |
| this.y += this.vy * dt * cfg.speed; | |
| this.angle += this.spin * dt * cfg.speed; | |
| const h = canvas.height / DPR; | |
| if(this.y < -40) this.reset(); | |
| } | |
| draw(ctx){ | |
| ctx.save(); | |
| ctx.globalAlpha = this.alpha; | |
| ctx.translate(this.x, this.y); | |
| ctx.rotate(this.angle); | |
| ctx.scale(this.scale, this.scale); | |
| drawRichText(ctx, this.text, 0, 0); | |
| ctx.restore(); | |
| } | |
| } | |
| function drawRichText(ctx, str, x, y){ | |
| const {tag, attr, val, text} = cfg.colors; | |
| const parts = str.split(/(\{(?:tag|attr|val)\}|\{\/\})/g); | |
| let col = text; let cursor = x; | |
| ctx.font = '14px ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace'; | |
| for(const p of parts){ | |
| if(p === '{tag}') { col = tag; continue; } | |
| if(p === '{attr}') { col = attr; continue; } | |
| if(p === '{val}') { col = val; continue; } | |
| if(p === '{/}') { col = text; continue; } | |
| ctx.fillStyle = col; | |
| ctx.fillText(p, cursor, y); | |
| cursor += ctx.measureText(p).width; | |
| } | |
| } | |
| function drawHand(ctx, x, y, t, left=true){ | |
| ctx.save(); | |
| ctx.translate(x, y); | |
| const lift = Math.sin(t * 6 + (left?0:Math.PI)) * 3; | |
| ctx.translate(0, lift); | |
| ctx.fillStyle = '#f1c8a0'; | |
| ctx.beginPath(); ctx.ellipse(0,0,10,8,0,0,TAU); ctx.fill(); | |
| for(let i=0;i<4;i++){ | |
| ctx.beginPath(); ctx.roundRect(-10 + i*6, -6, 5, 12, 3); ctx.fill(); | |
| } | |
| ctx.restore(); | |
| } | |
| function drawPerson(ctx, baseX, baseY, t){ | |
| ctx.save(); | |
| ctx.translate(baseX, baseY); | |
| ctx.globalAlpha = .15; | |
| ctx.fillStyle = '#000'; | |
| ctx.filter = 'blur(8px)'; | |
| ctx.beginPath(); ctx.ellipse(0, 62, 120, 18, 0, 0, TAU); ctx.fill(); | |
| ctx.filter = 'none'; ctx.globalAlpha = 1; | |
| ctx.fillStyle = '#1f2937'; | |
| ctx.beginPath(); ctx.roundRect(-70, -10, 140, 90, 16); ctx.fill(); | |
| ctx.fillStyle = '#e0b386'; | |
| ctx.fillRect(-12, -26, 24, 18); | |
| ctx.fillStyle = '#d6a06a'; | |
| ctx.beginPath(); ctx.ellipse(0, -50, 28, 34, 0, 0, TAU); ctx.fill(); | |
| ctx.fillStyle = '#171717'; | |
| ctx.beginPath(); | |
| ctx.moveTo(-28,-56); ctx.quadraticCurveTo(0,-75,28,-56); ctx.quadraticCurveTo(10,-34,-10,-30); ctx.quadraticCurveTo(-22,-34,-28,-56); ctx.fill(); | |
| ctx.globalAlpha = .15; ctx.fillStyle = '#0f0f0f'; | |
| ctx.beginPath(); ctx.ellipse(0, -34, 20, 14, 0, 0, TAU); ctx.fill(); | |
| ctx.globalAlpha = 1; | |
| ctx.fillStyle = '#0b1020'; | |
| ctx.fillRect(-14,-48,10,2); | |
| ctx.fillRect(4,-48,10,2); | |
| ctx.fillStyle = '#111827'; | |
| ctx.beginPath(); ctx.ellipse(-9,-44,3,2,0,0,TAU); ctx.fill(); | |
| ctx.beginPath(); ctx.ellipse(9,-44,3,2,0,0,TAU); ctx.fill(); | |
| ctx.strokeStyle = '#a16207'; ctx.lineWidth = 1.2; | |
| ctx.beginPath(); ctx.moveTo(0,-42); ctx.quadraticCurveTo(2,-38,0,-35); ctx.stroke(); | |
| ctx.fillStyle = '#0b1224'; | |
| ctx.fillRect(-180, 40, 360, 10); | |
| ctx.fillStyle = '#0f172a'; | |
| ctx.beginPath(); ctx.roundRect(-70, 8, 140, 80, 10); ctx.fill(); | |
| ctx.save(); | |
| const flicker = 0.08 + 0.04*Math.sin(t*8); | |
| const grd = ctx.createLinearGradient(-70,8,-70,88); | |
| grd.addColorStop(0, `rgba(96,165,250,${0.15+flicker})`); | |
| grd.addColorStop(1, 'rgba(0,0,0,0)'); | |
| ctx.fillStyle = grd; ctx.fillRect(-70,8,140,80); | |
| ctx.restore(); | |
| ctx.fillStyle = '#2563eb'; | |
| ctx.font = 'bold 14px ui-monospace, monospace'; | |
| ctx.fillText("</>", -60, 22); | |
| ctx.fillStyle = '#0a0f1d'; ctx.fillRect(-80, 88, 160, 8); | |
| drawHand(ctx, -24, 86, t, true); | |
| drawHand(ctx, 24, 86, t, false); | |
| ctx.restore(); | |
| } | |
| const sparks = Array.from({length: 80}, () => ({ | |
| x: Math.random(), y: Math.random(), r: Math.random()*1.5+0.3, s: Math.random()*0.6+0.2 | |
| })); | |
| const particles = Array.from({length: 28}, ()=> new CodeParticle()); | |
| let last = performance.now(); | |
| function loop(now){ | |
| const dt = Math.min(33, now - last) / 16.6667; | |
| last = now; | |
| const w = canvas.width / DPR, h = canvas.height / DPR; | |
| ctx.clearRect(0,0,w,h); | |
| ctx.globalAlpha = .06; | |
| for(let i=0;i<w;i+=32){ ctx.fillStyle = i%64===0? '#fff' : '#000'; ctx.fillRect(i, 0, 1, h);} | |
| for(let j=0;j<h;j+=32){ ctx.fillStyle = j%64===0? '#fff' : '#000'; ctx.fillRect(0, j, w, 1);} | |
| ctx.globalAlpha = 1; | |
| ctx.save(); | |
| for(const s of sparks){ | |
| const px = s.x * w, py = (s.y * h + (now*0.01*s.s)) % h; | |
| ctx.globalAlpha = .25 + .25*Math.sin((now*0.004)+(px+py)); | |
| ctx.fillStyle = '#93c5fd'; | |
| ctx.fillRect(px, py, s.r, s.r); | |
| } | |
| ctx.restore(); | |
| drawPerson(ctx, w*0.68, h*0.62, now/1000); | |
| ctx.save(); | |
| ctx.translate(w*0.28, h*0.38); | |
| for(const p of particles){ p.step(dt); p.draw(ctx); } | |
| ctx.restore(); | |
| requestAnimationFrame(loop); | |
| } | |
| requestAnimationFrame(loop); | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment