Canvas 2D fillStyle doesn't updates the color sometimes
Canvas 2D fillStyle doesn't updates the color sometimes
I'm learning canvas with the 2D breakout tutorial from MDN and there is an exercise: try changing the color of the ball to a random colour every time it hits the wall.
Before draw the ball, I check if the ball it's inside the wall. And if it touches the ball, first I generate a random hexadecimal value color, and then I draw the ball with that color.
But it only works sometimes. I've logged the fillStyle property of the ctx object but sometimes it doesn't change the value to the new color.
fillStyle
ctx
/* Reference to Canvas */
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
/* Start Ball Position */
let x = canvas.width / 2;
let y = canvas.height - 30;
/* Move Ball */
let dx = 2;
let dy = -2;
/* Ball Dimensions */
let ballRadius = 10;
/* Ball Color */
let ballColor = "#0095DD";
/* Draw Ball */
function drawBall()
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = ballColor;
ctx.fill();
ctx.closePath();
x += dx;
y += dy;
/* Update Canvas */
function draw()
ctx.clearRect(0, 0, canvas.width, canvas.height);
bouncing();
drawBall();
/**
* If the ball goes outside the boundaries,
* change the direction to the opposite
*/
function bouncing()
/* Change the ball color to a random hex value */
function randomColor()
const hexColor = ['#'];
const letters = ['A', 'B', 'C', 'D', 'E', 'F'];
for(let digit = 0; digit < 6; digit++)
let value = Math.floor(Math.random() * 16);
if(value > 9)
value = letters[value - 9];
hexColor.push(value);
ballColor = hexColor.join('');
setInterval(draw, 10);
html
box-sizing: border-box;
*, *::before, *::after
box-sizing: inherit;
padding: 0;
margin: 0;
canvas
background: #eee;
display: block;
margin: 0 auto;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>2D breakout game</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="myCanvas" width="480" height="170"></canvas>
<script src="script.js"></script>
</body>
</html>
function randomColor() ballColor = '#' + Math.floor(Math.random() * 0x1000000).toString(16).padStart(6, '0');
Typo: Your random generator produces invalid results (4-5 hex)... Because Array are 0 indexed and that
15-9=>6 hence letters[6] is undefined. But since you seem to use ES6, go with @Patrick's solution, cleaner and better.– Kaiido
Sep 3 at 0:41
15-9
letters[6]
@Kaiido Can you elaborate a bit more on the invalid results, I do get valid result out of that as is.
– Helder Sepulveda
Sep 3 at 0:51
@HelderSepu force value to be a
15– Kaiido
Sep 3 at 0:59
15
@Kaiido Ye that will cause value to be set to undefined and the join will output something like
#3856 maybe not the intended behavior, but that wont cause any error... the only issue I see is jumping into a color that is very similar to the previous one– Helder Sepulveda
Sep 3 at 1:11
#3856
2 Answers
2
The problem is in the line value = letters[value - 9];. This should be value = letters[value - 10];
value = letters[value - 9];
value = letters[value - 10];
This is because the mapping you want is:
10 -> A (letters[0])
11 -> B (letters[1])
12 -> C (letters[2])
13 -> D (letters[3])
14 -> E (letters[4])
15 -> F (letters[5])
So you need to subtract 10 not 9.
Your code is working fine, the function randomColor is called every-time the ball bounces, but in the color generation logic you can get invalid values such as #, #A or #AB you can easily fix that change the * 16 to just * 15 and that fixes that.
randomColor
#
#A
#AB
* 16
* 15
But also notice that sometimes your random color is very very close to the previous one, so it looks like it did not change, but in fact it did change, This is what I believe we are seen most of the time.
You could come up with a "smart random" function that remembers the last 2 or 3 colors and prevents the next from been similar to those, for more details how to compare colors look the answers to this question:
How to compare two colors for similarity/difference
Here I changed the logic of your randomColor to include color difference logic, now the next color will really show contrast from the previous one
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.font = "Bold 30px courier new";
let x = y = 40
let dx = dy = 2;
let ballRadius = 40;
let ballColor = "#0095DD";
/* Change the ball color to a random hex value */
function randomColor()
const hexColor = ['#'];
const letters = ['A', 'B', 'C', 'D', 'E', 'F'];
for(let digit = 0; digit < 6; digit++)
let value = Math.floor(Math.random() * 15);
if(value > 9)
value = letters[value - 9];
hexColor.push(value);
if (colorDif(ballColor, hexColor.join('')) > 50)
ballColor = hexColor.join('');
else
randomColor();
function colorDif(color1, color2)
if (color1 == color2) return 0;
function squaredDelta(v1, v2)
return Math.pow(v1 - v2, 2);
function hexToRgb(hex)
var result = /^#?([a-fd]2)([a-fd]2)([a-fd]2)$/i.exec(hex);
return result ?
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
: null;
var sum = 0;
var c1 = hexToRgb(color1);
var c2 = hexToRgb(color2);
sum += squaredDelta(c1.r, c2.r);
sum += squaredDelta(c1.g, c2.g);
sum += squaredDelta(c1.b, c2.b);
var conversionIndex = 19.5075;
return Math.sqrt(sum / conversionIndex);
;
function drawBall()
ctx.beginPath();
ctx.fillText(ballColor,10,50);
ctx.fillStyle = ballColor;
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fill();
x += dx; y += dy;
function draw()
ctx.clearRect(0, 0, canvas.width, canvas.height);
bouncing();
drawBall();
function bouncing()
setInterval(draw, 10);
*, *::before, *::after margin: 0;
<canvas id="myCanvas" width="480" height="170"></canvas>
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
The random color generation could be simplified a lot:
function randomColor() ballColor = '#' + Math.floor(Math.random() * 0x1000000).toString(16).padStart(6, '0');– Patrick Roberts
Sep 2 at 18:06