// Original source: // https://www.freecodecamp.org/news/how-to-create-animated-bubbles-with-html5-canvas-and-javascript/ const canvas = document.createElement('canvas') canvas.style.backgroundColor = '#00b4ff' document.body.appendChild(canvas) canvas.width = window.innerWidth canvas.height = window.innerHeight const context = canvas.getContext("2d") context.font = "30px Arial" context.textAlign = 'center' context.fillStyle = 'white' context.fillText('Click to spawn bubbles', canvas.width/2, canvas.height/2) let circles = [] function draw(circle) { context.beginPath() context.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI) context.strokeStyle = `hsl(${circle.hue} 100% 50%)` context.stroke() const gradient = context.createRadialGradient( circle.x, circle.y, 1, circle.x + 0.5, circle.y + 0.5, circle.radius ) gradient.addColorStop(0.3, "rgba(255, 255, 255, 0.3)") gradient.addColorStop(0.95, "#e7feff") context.fillStyle = gradient context.fill() } function move(circle, timeDelta) { circle.x = circle.x + timeDelta*circle.dx circle.y = circle.y - timeDelta*circle.dy } let intervalId function startAnimation() { if(intervalId == null) { intervalId = setInterval(animate, 20) } } function stopAnimation() { if(intervalId != null) { clearInterval(intervalId) intervalId = null } } let prevFrameTime const animate = () => { const now = Date.now() const timeDelta = prevFrameTime == null ? 0 : now - prevFrameTime prevFrameTime = now if(circles.length == 0) { return } context.clearRect(0, 0, canvas.width, canvas.height) circles.forEach(circle => { move(circle, timeDelta) draw(circle) }) } const createCircles = (event) => { startAnimation() circles = circles.concat(Array.from({length: 50}, () => ( { x: event.pageX, y: event.pageY, radius: Math.random() * 50, dx: Math.random() * 0.3, dy: Math.random() * 0.7, hue: 200, } ))) } canvas.addEventListener("click", createCircles) window.onfocus = startAnimation window.onblur = stopAnimation