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>





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


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.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

How do I collapse sections of code in Visual Studio Code for Windows?

Node.js puppeteer - Use values from array in a loop to cycle through pages