如何解决Canvas绘制气泡时全部合并成一条线的问题
问题原因及修复方案
核心问题1:数值类型错误
generateDecimalBetween函数使用toFixed(2)返回的是字符串类型,但气泡的位置、大小等属性需要是数值类型。字符串参与减法运算时会触发错误的类型转换,导致位置计算完全异常,最终所有气泡挤在同一区域形成线条。
修复该函数,返回数值类型:
function generateDecimalBetween(max, min) { return parseFloat((Math.random() * (max - min) + min).toFixed(2)); }
核心问题2:未定义的画布尺寸变量
animate方法中调用的this.canvasWidth和this.canvasHeight从未被赋值,导致clearRect无法正确清除画布,残留的绘制内容会让气泡看起来连成线。需要在canvasSize方法中保存这两个值:
canvasSize(){ this.canvasWidth = this.canvas.offsetWidth; this.canvasHeight = this.canvas.offsetHeight; this.canvas.width = this.canvasWidth * this.dpr; this.canvas.height = this.canvasHeight * this.dpr; this.ctx.scale(this.dpr, this.dpr); }
修复后的完整代码
const COLORS = ["255,108,80", "5,117,18", "29,39,57", "67,189,81"]; const BUBBLE_DENSITY = 100; function generateDecimalBetween(max, min) { return parseFloat((Math.random() * (max - min) + min).toFixed(2)); } function generateColor(max, min) { return Math.floor(Math.random() * (max - min) + min); } class Bubble { constructor(canvas) { this.canvas = canvas; this.getCanvasSize(); this.init(); } getCanvasSize() { this.canvasWidth = this.canvas.offsetWidth; this.canvasHeight = this.canvas.offsetHeight; } init() { this.color = generateColor(COLORS.length, 0); this.size = generateDecimalBetween(3, 1); this.alpha = generateDecimalBetween(10, 5) / 10; this.translateX = generateDecimalBetween(this.canvasWidth, 0); this.translateY = generateDecimalBetween(this.canvasHeight, 0); this.velocity = generateDecimalBetween(40, 20); this.movementX = generateDecimalBetween(2, -2) / this.velocity; this.movementY = generateDecimalBetween(20, 1) / this.velocity; } move() { this.translateX -= this.movementX; this.translateY -= this.movementY; if (this.translateY < 0 || this.translateX < 0 || this.translateX > this.canvasWidth) { this.init(); this.translateY = this.canvasHeight; } } } class canvasBackground{ constructor(id){ this.canvas = document.getElementById(id); this.ctx = this.canvas.getContext('2d'); this.dpr = window.devicePixelRatio || 1; } start(){ this.canvasSize(); this.generateBubbles(); this.animate(); } canvasSize(){ this.canvasWidth = this.canvas.offsetWidth; this.canvasHeight = this.canvas.offsetHeight; this.canvas.width = this.canvasWidth * this.dpr; this.canvas.height = this.canvasHeight * this.dpr; this.ctx.scale(this.dpr, this.dpr); } generateBubbles(){ this.bubblesList = []; for (let i = 0; i < BUBBLE_DENSITY; i++) { this.bubblesList.push(new Bubble(this.canvas)); } } animate() { this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight); this.bubblesList.forEach(bubble => { this.ctx.save(); bubble.move(); this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0); this.ctx.translate(bubble.translateX, bubble.translateY); this.ctx.beginPath(); this.ctx.arc(0, 0, bubble.size, 0, Math.PI * 2); this.ctx.fillStyle = `rgba(${COLORS[bubble.color]},${bubble.alpha})`; this.ctx.fill(); this.ctx.restore(); }); requestAnimationFrame(this.animate.bind(this)); } } document.addEventListener('DOMContentLoaded', () => { const CanvasBackground = new canvasBackground('orb-canvas'); CanvasBackground.start(); });
内容的提问来源于stack exchange,提问作者Илья
相关产品推荐
相关产品推荐

