1const canvas = document.getElementById("particleCanvas"); 2const ctx = canvas.getContext("2d"); 3const button = document.querySelector(".download_btn"); 4 5const dpr = window.devicePixelRatio || 1; 6 7function setupCanvas() { 8 const rect = canvas.getBoundingClientRect(); 9 canvas.width = rect.width * dpr; 10 canvas.height = rect.height * dpr; 11 ctx.scale(dpr, dpr); 12 canvas.style.width = `${rect.width}px`; 13 canvas.style.height = `${rect.height}px`; 14} 15 16setupCanvas(); 17 18const particles = []; 19const emojis = ["ð©", "ð", "ð¨", "ð"]; 20const maxParticles = 100; 21let lastTriggerTime = 0; 22const triggerCooldown = 200; 23 24class Particle { 25 constructor(x, y) { 26 this.x = x; 27 this.y = y; 28 this.size = 32; 29 this.speedX = (Math.random() - 0.5) * 3; 30 this.speedY = Math.random() * -8; 31 this.gravity = 0.1; 32 this.emoji = emojis[Math.floor(Math.random() * emojis.length)]; 33 this.opacity = 1; 34 this.friction = 0.99; 35 this.angle = Math.random() * Math.PI * 2; 36 this.angleSpeed = (Math.random() - 0.5) * 0.1; 37 this.lifetime = 200; 38 } 39 40 update() { 41 this.speedY += this.gravity; 42 this.x += this.speedX; 43 this.y += this.speedY; 44 this.speedX *= this.friction; 45 this.speedY *= this.friction; 46 this.lifetime -= 1; 47 this.opacity = Math.max(0, this.lifetime / 200); 48 this.angle += this.angleSpeed; 49 } 50 51 draw() { 52 ctx.save(); 53 ctx.globalAlpha = this.opacity; 54 ctx.font = `${this.size}px Arial`; 55 ctx.translate(this.x, this.y); 56 ctx.rotate(this.angle); 57 ctx.fillText(this.emoji, 0, 0); 58 ctx.restore(); 59 } 60} 61 62function createParticles(x, y) { 63 const currentTime = Date.now(); 64 if (currentTime - lastTriggerTime < triggerCooldown) return; 65 lastTriggerTime = currentTime; 66 67 const particlesToCreate = Math.min(20, maxParticles - particles.length); 68 for (let i = 0; i < particlesToCreate; i++) { 69 particles.push(new Particle(x, y)); 70 } 71} 72 73function animateParticles() { 74 ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr); 75 76 for (let i = particles.length - 1; i >= 0; i--) { 77 particles[i].update(); 78 particles[i].draw(); 79 80 if (particles[i].lifetime <= 0) { 81 particles.splice(i, 1); 82 } 83 } 84 85 requestAnimationFrame(animateParticles); 86} 87 88button.addEventListener("mouseenter", () => { 89 const rect = button.getBoundingClientRect(); 90 const canvasRect = canvas.getBoundingClientRect(); 91 92 const x = rect.left + rect.width / 2 - canvasRect.left; 93 const y = rect.top + rect.height / 2 - canvasRect.top; 94 95 createParticles(x, y); 96}); 97 98animateParticles(); 99 100window.addEventListener("resize", () => { 101 setupCanvas(); 102});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.