使用Canvas绘图时,调用beginPath()和closePath()后仍无法设置不同fillStyle
解决Canvas元素颜色继承问题
我明白你遇到的困扰了——球总是跟着砖块的颜色走,没法用randomColor()生成专属的随机色。这其实是Canvas全局绘图状态特性加上你设置fillStyle的时机错误导致的,咱们一步步来修复:
问题根源拆解
你看原代码里的几个绘制函数,比如drawPaddle()和drawBricks(),都是先调用ctx.fill()再设置fillStyle——这完全搞反了顺序!Canvas的绘图属性(比如fillStyle)是先设置再生效的,先fill再改样式,等于这次绘制用的还是之前的全局样式,修改的样式只会影响下一次绘制。
另外,drawBall()里你在fill()之后才尝试设置fillStyle,这根本不会影响当前球的颜色;而且drawBricks()执行完后,全局fillStyle被设成了#00a6ff,下一次绘制球时,你没先给球设置专属颜色,自然就继承了砖块的颜色。
修改后的完整代码
我调整了fillStyle的设置顺序,还给球加了专门的颜色变量,方便碰撞时更新颜色:
var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); var x = canvas.width / 2; var y = canvas.height - 30; var dx = 2; var dy = -2; var ballRadius = 10; var paddleHeight = 10; var paddleWidth = 75; var paddleX = (canvas.width - paddleWidth) / 2; var rightPressed = false; var leftPressed = false; var brickRowCount = 3; var brickColumnCount = 5; var brickWidth = 75; var brickHeight = 20; var brickPadding = 10; var brickOffsetTop = 30; var brickOffsetLeft = 30; // 新增:给球单独设置颜色变量 var ballColor = randomColor(); document.addEventListener("keydown", keyDownHandler, false); document.addEventListener("keyup", keyUpHandler, false); function randomColor() { var n = 16777215; var colorDec = rand(0, n); var colorHex = colorDec.toString(16); // 补全6位Hex,避免生成不完整的颜色代码 return "#" + colorHex.padStart(6, '0'); } function rand(min, max) { var min = min || 0, max = max || Number.MAX_SAFE_INTEGER; return Math.floor(Math.random() * (max - min + 1)) + min; } var bricks = []; for (var c = 0; c < brickColumnCount; c++) { bricks[c] = []; for (var r = 0; r < brickRowCount; r++) { bricks[c][r] = { x: 0, y: 0, status: 1 }; } } function keyDownHandler(e) { if (e.keyCode == 39) { rightPressed = true; } else if (e.keyCode == 37) { leftPressed = true; } } function keyUpHandler(e) { if (e.keyCode == 39) { rightPressed = false; } else if (e.keyCode == 37) { leftPressed = false; } } function collisionDetection() { for (var c = 0; c < brickColumnCount; c++) { for (var r = 0; r < brickRowCount; r++) { var b = bricks[c][r]; if (b.status == 1) { if (x > b.x && x < b.x + brickWidth && y > b.y && y < b.y + brickHeight) { dy = -dy; b.status = 0; // 碰撞砖块时更新球的颜色 ballColor = randomColor(); } } } } } function drawBall() { ctx.beginPath(); ctx.arc(x, y, ballRadius, 0, Math.PI * 2); // 先设置球的颜色,再执行填充 ctx.fillStyle = ballColor; ctx.fill(); ctx.closePath(); } function drawPaddle() { ctx.beginPath(); ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight); // 先设置 paddle 颜色,再填充 ctx.fillStyle = "#00a6ff"; ctx.fill(); ctx.closePath(); } function drawBricks() { for (var c = 0; c < brickColumnCount; c++) { for (var r = 0; r < brickRowCount; r++) { if (bricks[c][r].status == 1) { var brickX = (c * (brickWidth + brickPadding)) + brickOffsetLeft; var brickY = (r * (brickHeight + brickPadding)) + brickOffsetTop; bricks[c][r].x = brickX; bricks[c][r].y = brickY; ctx.beginPath(); ctx.rect(brickX, brickY, brickWidth, brickHeight); // 先设置砖块颜色,再填充 ctx.fillStyle = "#00a6ff"; ctx.fill(); ctx.closePath(); } } } } function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); drawBall(); drawPaddle(); drawBricks(); collisionDetection(); if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) { dx = -dx; // 碰到左右边界时更新球色 ballColor = randomColor(); } if (y + dy < ballRadius) { dy = -dy; // 碰到上边界时更新球色 ballColor = randomColor(); } else if (y + dy > canvas.height - ballRadius) { if (x > paddleX && x < paddleX + paddleWidth) { dy = -dy; // 碰到 paddle 时更新球色 ballColor = randomColor(); } else { alert("GAME OVER"); document.location.reload(); } } if (rightPressed && paddleX < canvas.width - paddleWidth) { paddleX += 7; } else if (leftPressed && paddleX > 0) { paddleX -= 7; } y += dy; x += dx; } setInterval(draw, 10);
* { padding: 0; margin: 0; } canvas { background: #eee; display: block; margin: 0 auto; }
<canvas id="myCanvas" width="480" height="320"></canvas>
核心修改说明
- 调整
fillStyle顺序:所有绘制函数里,先设置fillStyle再调用ctx.fill(),确保当前绘制用的是元素专属颜色。 - 新增
ballColor变量:专门存储球的颜色,碰撞或碰到边界时更新这个变量,避免直接修改全局fillStyle干扰其他元素。 - 修复
randomColor():用padStart(6, '0')补全Hex字符串到6位,防止生成不完整的颜色代码(比如#123变成#000123)。
这样改完后,球会保持随机颜色,paddle和砖块固定为#00a6ff,再也不会出现颜色继承的问题啦!
内容的提问来源于stack exchange,提问作者Aditya Thakur
相关产品推荐
相关产品推荐

