Michael, your question has been on SO for some time but hopefully this answer might still be useful to you as well as others still learning JavaScript. Once I addressed basic syntax issues listed by Mat, the missing circles became visible. At that point I discovered three things.
images drawn on your canvas may not appear where you expect them.
a variable declared and initialised as number
in your first
statement does not generate a valid number let alone a random number.
your code has if
and if else
statements that will not do anything
without an event handler.
#1
Your code uses ctx.arc()
to draw a basic circle and ctx.translate()
to move it into position on the canvas. With ctx.translate()
two parameters x and y determine how far to move the circle; x is the distance to move in the horizontal direction; y is the distance to move in the vertical direction.
The problem is that you no longer know the position coordinates on the canvas after the move. For this reason the origin needs to be reset before proceeding to the next ctx.translate()
operation.
To reset the origin use CanvasRenderingContext2D.setTransform()
e.g.
ctx.setTransform(1, 0, 0, 1, 0, 0);
from Mozilla MDN
The CanvasRenderingContext2D.setTransform() method of the Canvas 2D
API resets (overrides) the current transformation to the identity
matrix, and then invokes a transformation described by the arguments
of this method. This lets you scale, rotate, translate (move), and
skew the context.
This API is designed for animated drawing with a variety of methods for transforming 2D shapes dynamically. For the purposes of your code it is enough to understand that by clearing the identity matrix you restore 0,0
(the canvas origin) as the reference for x and y values used in any drawing operation that follows. Before you use ctx.translate()
reset the origin. Watch the difference this will make when the code launches.
effect of origin reset at launch
without / with
Whenever a new image is drawn the entire canvas must first be cleared using ctx.clearRect()
and it is important to reset the origin before doing so. Click the 'Change Size' button in each example to see that the entire screen is cleared with an origin reset. Without a reset the origin of the cleared rectangle will align with the centre of the last circle drawn i.e. the cleared rectangle eats into the lower right quadrant of the circle.
effect of origin reset on clearing the canvas
without / with
#2
The second problem can be demonstrated by inserting alert(number)
following the first instruction. When the code runs, alert
reports a NaN
message (i.e. not a number). Also, random values lie within a range from 0.0 to 1.0 but for your purposes can be scaled to a range from 5.0 to 10.0 (for more see here and here).
The code below shows getNewRadius(),
a function that generates a random number whenever the 'Change Size' button is clicked. Before the function is called constants and variables are declared and initialised. Every time the function is called, i.e. with every mouse click, it returns a new random value of radius
which is then passed as an argument to the next drawing function.
For clarity I renamed your variable number
to the more self-explanatory radius
and replaced magic numbers in your code with some aptly named constant
or var
.
#3
Statements within the scope of your if
and else if
tests will never be executed. Testable values are potentially created at runtime by a keyboard event (i.e. event.keycode
). But in your code no handler has been set up to listen for such an event. Moreoever, Mozilla MDN advises that keyCode is obsolete and makes the following recommendation
MDN Warning: This attribute is deprecated; you should use
KeyboardEvent.key instead, if available.
For a more comprehensive summary of keyboard event options see here.
The code below includes a basic function called setMaxOrMinRadius(e)
that allows maximum or minimum radius values to be selected by typing on a keyboard. In keeping with your initial code, typing s
will select minRadius
, typing b
will select maxRadius
. This function will only be recognised if a key is pressed and the event listener has been initialised. If the event listener detects a keystroke it will now be able to switch to the function and recognise which key has been pressed.
Acknowledgement
This answer is a collaborative effort between fellow retiree and former colleague Bob Douglas and myself. We have been learning JavaScript together for two months. We welcome suggestions for improvement.
Code
<canvas width="300" height="300" id="myCanvas" style="border: 1px solid black"></canvas>
<button id="changeRadius">Change Size</button>
<script>
const maxRadius = 10;
const minRadius = 5;
var range = maxRadius - minRadius;
var radius = maxRadius;
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
document.addEventListener('keydown', setMaxOrMinRadius);
drawCircles(maxRadius);
document.getElementById("changeRadius").onclick = function() {
clearCanvas();
radius = getNewRadius();
drawCircles(radius);
}
function setMaxOrMinRadius(e) {
clearCanvas();
switch (e.which) {
case 83: // type 's'
drawCircles(minRadius);
break;
case 66: // type 'b'
drawCircles(maxRadius);
break;
}
}
function getNewRadius() {
var radius = (Math.random() * minRadius) + range;
return radius;
}
function drawCircles(radius) {
drawBlueCircle(radius);
drawBlackCircle(radius);
}
function clearCanvas(){
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset origin
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
function drawBlackCircle (radius) {
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset origin
ctx.translate(100,120);
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.fillStyle = "black";
ctx.fill();
}
function drawBlueCircle (radius) {
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset origin
ctx.translate(140,120);
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
}