//Selectors
canvas = document.querySelector('.canvas');
canvas.setAttribute("tabindex", 0);
canvas.focus();
pointerImg = document.querySelector('.pointer');
//Variables
const pi = Math.PI;
const c = canvas.getContext('2d');
var grid = {
'x':60,
'y':20,
'size':20,
}
var pointerValues = {
37: {'x':-1, 'y':0},
38: {'x':0, 'y':-1},
39: {'x':1, 'y':0},
40: {'x':0, 'y':1},
}
//Event Listeners
window.addEventListener('resize', function() {
//init();
})
var cells = [];
for(let i=0;i<grid.x;i++) {
cells[i] = new Array(grid.y);
}
//Functions
function init() {
resizeGrid();
initCells();
}
init();
function update(){
requestAnimationFrame(update)
}
update();
function Cell(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.color = 'white';
this.update = function() {
this.draw();
}
this.draw = function() {
c.beginPath();
c.rect(this.x*this.size, this.y*this.size, this.size, this.size);
c.strokeStyle = 'black';
c.stroke();
c.fillStyle=this.color; c.fill();
c.closePath();
}
this.resetCell = function() {
c.clearRect(this.x, this.y, this.size, this.size);
this.update();
}
}
function resizeGrid() {
let windowX = window.innerWidth;
windowX -= windowX*0.1;
grid.size = windowX/grid.x;
let canvasX = windowX, canvasY = grid.y * grid.size;
canvas.width = canvasX;
canvas.height = canvasY;
}
function initCells() {
cells = [];
for(let i=0;i<grid.x;i++) {
cells[i] = new Array(grid.y);
}
for(let i=0;i<grid.x;i++) {
for(let j=0;j<grid.y;j++) {
cells[i][j] = new Cell(i, j, grid.size);
cells[i][j].update();
}
}
}
<html>
<head>
<link rel="stylesheet" href="assets/css/main.css">
<div style="display:none;">
<img class='pointer' src="assets/img/pointer.svg">
</div>
<div class='nav'>
</div>
</head>
<body>
<div class="content">
<canvas class='canvas'></canvas>
</div>
<script src='assets/js/canvas.js'></script>
</body>
</html>
function Cell(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.color = 'white';
this.update = function () {
this.draw();
}
this.draw = function () {
c.beginPath();
c.rect(this.x * this.size, this.y * this.size, this.size, this.size);
c.strokeStyle = 'black';
c.stroke();
c.fillStyle = this.color;
c.fill();
c.closePath();
}
this.resetCell = function () {
c.clearRect(this.x, this.y, this.size, this.size);
this.update();
}
This is my code for a single cell in a grid. After I draw it a single time, when I call resetCell(), it draws the cell just a little bit bigger.
In this picture, I call Update for the very first cell and it becomes a tiny bit bigger than the rest.
I've tried pretty much everything but nothing seems to work.
Also can someone recommend better way to draw and control grid.
I need it to demonstrate BFS algorithm.
You were tricked by the thickness of the border
The border has a thickness, which is easy to forget. If you draw a box which is X wide, its left and right border are in addition to the width of X.
So if you do NOT fill the interior, you get this nice appearance (left), but if you FILL, you get this ugly appearance (right).
When you draw a grid of these squares, each one covers over the right-hand and bottom sides of previous squares, so that it is not obvious what is happening.
Unless you redraw one that is not the last of the list, as I have done here (bottom-middle). Then it becomes obvious that something is wrong.
Here is the code to reproduce the above figures, and below I recommend a solution.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// With NO fill, it looks fine
ctx.beginPath();
ctx.rect(40, 40, 40, 40);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.rect(80, 80, 40, 40);
ctx.stroke();
ctx.closePath();
// With white fill, the inner half of each box gets covered up by the white fill.
ctx.beginPath();
ctx.rect(140, 40, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(180, 80, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
// Make Grid. This part looks OK initially.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.rect(240, 40, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(240, 80, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(280, 40, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(280, 80, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(320, 40, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.rect(320, 80, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
// Redraw one: this will look messed up.
ctx.beginPath();
ctx.rect(280, 80, 40, 40);
ctx.stroke();
ctx.fillStyle="white";
ctx.fill();
ctx.closePath();
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="400" height="150" ></canvas>
</body>
</html>
Solution 1. Don't fill the interiors
This is the neatest solution, if you can get away with it, and it works regardless of any (non-zero) border thickness.
Solution 2. Shrink the width to allow for border thickness
This is complicated because you need to manually set the border thickness, and realise that it is in addition to the numerical values you specify for the rectangle's size.
Look at this messy business at CSS-tricks, even when they are trying to make borders interesting! https://css-tricks.com/almanac/properties/b/border/
Related
I'm trying to modify some getElementById code I found on the internet to write a function that uses a switch/case statement so that I can draw different shapes using the same function. Basically, I want to create a tiny state machine for shape drawing commands. JavaScript is still new to me, so I'm probably missing something glaringly obvious. Cheers!
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Draw a circle</title>
<script src="draw_shapes.js"></script>
</head>
<body onload="draw01();">
<canvas id="circle" width="150" height="150"></canvas>
</body>
</html>
draw_shapes.js
function draw01() {
var canvas = document.getElementById('circle');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
var X = canvas.width / 2;
var Y = canvas.height / 2;
var R = 45;
ctx.beginPath();
ctx.arc(X, Y, R, 0, 2 * Math.PI, false);
ctx.lineWidth = 3;
ctx.strokeStyle = '#000000';
ctx.stroke();
}
}
function draw02() {
const canvas = document.getElementById(canvas.id);
const ctx = canvas.getContext('2d');
switch (canvas.id) {
case "circle":
var X = canvas.width / 2;
var Y = canvas.height / 2;
var R = 45;
ctx.beginPath();
ctx.arc(X, Y, R, 0, 2 * Math.PI, false);
ctx.lineWidth = 3;
ctx.strokeStyle = '#000000';
ctx.stroke();
break;
}
}
Second iteration. Still not quite working right, but I've got the setup more as I was envisioning.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Draw a circle</title>
<script src="draw_shapes_02.js"></script>
</head>
<body onload="draw(CIRCLE);">
</body>
</html>
draw_shapes_02.js
//shape enumeration (an object with constant variables with immutable property values)
const shapes = {
CIRCLE: "circle",
RECTANGLE: "rectangle",
TRIANGLE: "triangle",
HOUSE: "house",
DEFAULT:"default",
}
Object.freeze(shapes);
// shape default setting and error
let shape = shapes.DEFAULT;
if (!shape) {
throw new Error("Shape is not defined");
}
// shape state machine
function draw(shapes) {
var canvas = document.getElementById("shape");
var ctx = canvas.getContext('2d');
switch ("shape") {
case shapes.CIRCLE:
ctx.beginPath();
ctx.fillStyle = "black";
ctx.arc(150, 60, 50, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
break;
case shapes.RECTANGLE:
ctx.beginPath();
ctx.fillStyle = "green";
ctx.rect(100, 130, 100, 50);
ctx.fill();
ctx.closePath();
break;
case shapes.TRIANGLE:
ctx.beginPath();
ctx.fillStyle = "yellow";
ctx.moveTo(150, 200);
ctx.lineTo(200, 250);
ctx.lineTo(100, 250);
ctx.lineTo(150, 200);
ctx.fill();
ctx.closePath();
break;
case shapes.HOUSE:
ctx.lineWidth = 10;
ctx.strokeRect(75, 140, 150, 110);
ctx.fillRect(130, 190, 40, 60);
ctx.beginPath();
ctx.moveTo(50, 140);
ctx.lineTo(150, 60);
ctx.lineTo(250, 140);
ctx.closePath();
ctx.stroke();
break;
case shapes.DEFAULT:
break;
}
}
I have simplified a bit but you can complicate again as you wish :)
function draw(shape) {
var canvas = document.getElementById("CIRCLE");
var ctx = canvas.getContext('2d');
console.log(shape);
switch (shape) {
case "CIRCLE":
ctx.beginPath();
ctx.fillStyle = "black";
ctx.arc(150, 60, 50, 0, 2 * Math.PI);
ctx.closePath();
ctx.fill();
break;
case "RECTANGLE":
ctx.beginPath();
ctx.fillStyle = "green";
ctx.rect(100, 130, 100, 50);
ctx.fill();
ctx.closePath();
break;
case "TRIANGLE":
ctx.beginPath();
ctx.fillStyle = "yellow";
ctx.moveTo(150, 200);
ctx.lineTo(200, 250);
ctx.lineTo(100, 250);
ctx.lineTo(150, 200);
ctx.fill();
ctx.closePath();
break;
case "HOUSE":
ctx.lineWidth = 10;
ctx.strokeRect(75, 140, 150, 110);
ctx.fillRect(130, 190, 40, 60);
ctx.beginPath();
ctx.moveTo(50, 140);
ctx.lineTo(150, 60);
ctx.lineTo(250, 140);
ctx.closePath();
ctx.stroke();
break;
default:
ctx.fillStyle = "#FF0000";
ctx.fillRect(0,0,150,75);
break;
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Draw a circle</title>
</head>
<body onload="draw('HOUSE');">
<canvas width="240" height="297" style="border:1px solid #d3d3d3;" id="CIRCLE"></canvas>
</body>
</html>
I want a ball falling, with gravity. But the background won't clear the previous circle. (the falling part isnt implemented yet) why doesnt it erase the last drawn object?
let c, ctx;
let met;
let colorArray = ["green", "yellow", "orange", "red"];
window.onload = function(){
c = document.getElementById("boom");
ctx = c.getContext("2d");
c.width = window.innerWidth - 165;
c.height = window.innerHeight;
setup();
draw();
}
function setup(){
met = new Meteor(200, 400, 10, 0, 5);
}
function draw(){
window.requestAnimationFrame(draw);
ctx.fillStyle = colorArray[Math.floor(Math.random() * colorArray.length)];
ctx.fillRect(0, 0, c.width, c.height);
met.draw();
met.fall();
};
class Meteor {
constructor(x, y, r, xSpeed, ySpeed){
this.xPos = x;
this.yPos = y;
this.radius = r;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.color = "blue";
this.acceleration = 0.5;
}
draw(){
ctx.fillStyle = this.color;
ctx.arc(this.xPos, this.yPos, this.radius, 0, 2 * Math.PI);
//ctx.fill();
ctx.stroke();
}
fall(){
this.yPos += (this.ySpeed);
}
}
Also, if you have any tips on how to make it have gravity, please don't hesitate to tell me. Ive been struggling with it for a while now.
In draw() add this before the call to met.draw():
ctx.clearRect(0, 0, window.innerWidth, window.innterHeight)
This will clear the screen every frame.
For gravity, add a dt parameter to your draw function, then pass it to met.draw() like this:
function draw(dt){
window.requestAnimationFrame(draw);
ctx.fillStyle = colorArray[Math.floor(Math.random() * colorArray.length)];
ctx.fillRect(0, 0, c.width, c.height);
ctx.clearRect(0, 0, window.innerWidth, window.innterHeight) // Clear screen here
met.draw(dt);
met.fall();
};
Then in the Meteor class, use the dt in the draw() function to calculate your y position based on velocity. Here dt will be your delta time for the calculation.
I'm trying to move the tank when the user pressed the right/left key. When I use rotate() it is affecting all the elements of the canvas. Is there a way to only move the third rectangle of the tank when the user presses the right or left key?
The goal is to change the angle by a certain amount on pressing the keys. I searched, but couldn't find something basic enough for me to understand how to implement it.
var canvas=document.getElementById('canvas');
var ctx=canvas.getContext('2d');
var rightPressed=false;
var leftPressed=false;
var score1=0;
var score2=0;
var turn=0;
document.addEventListener("keydown",keyDownHandler,false);
document.addEventListener("keyup",keyUpHandler,false);
function keyDownHandler(e) {
if(e.keyCode==37)
leftPressed=true;
else if(e.keyCode==39)
rightPressed=true;
}
function keyUpHandler(e) {
if(e.keyCode==37)
leftPressed=false;
else if(e.keyCode==39)
rightPressed=false;
}
function drawMountain() {
ctx.beginPath();
ctx.moveTo(250, 400);
ctx.bezierCurveTo(250, 100, 500, 100, 500, 400);
ctx.fillStyle="green";
ctx.fill();
ctx.closePath();
}
function drawTanks() {
ctx.beginPath();
ctx.rect(20,360,50,40);
ctx.rect(620,360,50,40);
ctx.rect(30,340,30,20);
ctx.rect(630,340,30,20);
ctx.rect(40,300,10,40);
ctx.rect(640,300,10,40);
ctx.fillStyle="blue";
ctx.fill();
ctx.closePath();
}
function rotateTank() {
ctx.beginPath();
ctx.rect(20,20,130,40);
ctx.rect(560,20,130,40);
ctx.fillStyle="green";
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.fillStyle="white";
ctx.font="20px Verdana";
ctx.fillText('Player 1: '+ score1,28,45);
ctx.closePath();
ctx.beginPath();
ctx.fillStyle="white";
ctx.font="20px Verdana";
ctx.fillText('Player 2: '+ score2,570,45);
ctx.closePath();
}
function drawGame() {
ctx.clearRect(0,0,canvas.width,canvas.height);
drawMountain();
drawTanks();
rotateTank();
}
setInterval(drawGame,10);
#canvas {
background-color: black;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="game.css">
</head>
<body>
<center><canvas id="canvas" width="700" height="400"></canvas></center>
<script src="game.js">
</script>
</body>
</html>
Let's say that we have two rectangles.
ctx.fillStyle = "#F00";
ctx.fillRect(10, 10, 20, 10);
ctx.fillStyle = "#00F";
ctx.fillRect(50, 50, 20, 10);
And you want to rotate only the first one.
There are two ways to do it:
Move the red rectangle after the blue one and rotate it
ctx.fillStyle = "#00F";
ctx.fillRect(50, 50, 20, 10);
ctx.rotate(20*Math.PI/180);
ctx.fillStyle = "#F00";
ctx.fillRect(10, 10, 20, 10);
Just set ctx.rotate to a negative number (in our case -20) after the red rectangle
ctx.rotate(20*Math.PI/180);
ctx.fillStyle = "#F00";
ctx.fillRect(10, 10, 20, 10);
ctx.rotate(-20*Math.PI/180); //This "resets" the rotation.
ctx.fillStyle = "#00F";
ctx.fillRect(50, 50, 20, 10);
Here's a code snippet:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
function draw() {
ctx.rotate(20*Math.PI/180);
ctx.fillStyle = "#F00";
ctx.fillRect(10, 10, 20, 10);
ctx.rotate(-20*Math.PI/180);
ctx.fillStyle = "#00F";
ctx.fillRect(50, 50, 20, 10);
}
draw();
<canvas id="canvas" width=100 height=100 style="border: 1px solid #000"></canvas>
I am in a computer science class at my high school, and for my final project, my partner and I have decided to make 2048. We are not going to have the tiles "move", but instead, the 4x4 board will have "tiles" that are have properties of the number and color of the "tile" as it appears on the board. We are able to get the x and y coordinates of a tile to change, but the canvas will not update. We have had this problem for two weeks now, and have tried everything, but nothing has worked. Our code is posted below.
Please keep in mind that as we have only used javascript for a couple months, and only to do very simple things, our understanding is limited. Thank you for your help.
<!DOCTYPE html>
<html lang = "en">
<body style="background-color:lightgrey;">
<title>2048</title>
<canvas id="myCanvas" width="410" height="410" style="border:1px solid #d3d3d3;">
</canvas>
<script>
var x = 10;
var y = 10;
document.addEventListener('keydown', function(event)
{
if(event.keyCode == 37)//left
{
x -= 100;
console.log("x:"+ x);
}
else if(event.keyCode == 38)//up
{
y -= 100;
console.log("y:"+ y);
}
else if(event.keyCode == 39)//right
{
x += 100;
console.log("x:"+ x);
}
else if(event.keyCode == 40)//down
{
y += 100;
console.log("y:"+ y);
}
});
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// grey rectangle
ctx.beginPath();
ctx.lineWidth = "2";
ctx.strokeStyle = "grey";
ctx.rect(5, 5, 400, 400);
ctx.stroke();
// blue rectangle
ctx.beginPath();
ctx.lineWidth = "5";
ctx.strokeStyle = "blue";
ctx.rect(x, y, 100, 100);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
</script>
</body>
</html>
What your game needs is a game loop. So the drawing needs to happen continously, at the moment you draw only one time at the beginning.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
function draw() {
// clear the canvas so the old rectangles are gone
ctx.clearRect(0, 0, c.width, c.height);
// grey rectangle
ctx.beginPath();
ctx.lineWidth = "2";
ctx.strokeStyle = "grey";
ctx.rect(5, 5, 400, 400);
ctx.stroke();
// blue rectangle
ctx.beginPath();
ctx.lineWidth = "5";
ctx.strokeStyle = "blue";
ctx.rect(x, y, 100, 100);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
}
// repeat game loop function forever, 30 times per second
window.setInterval(draw, 1000.0 / 30.0)
Notice the use of window.setInterval and ctx.clearRect.
Every time you want to update what is shown on the canvas, you have to 'Draw' again. Your code will run once when the page loads but never draws again so your changes to x and y are never seen.
I added a draw function here that is called on startup and after each keydown.
<!DOCTYPE html>
<html lang = "en">
<body style="background-color:lightgrey;">
<title>2048</title>
<canvas id="myCanvas" width="410" height="410" style="border:1px solid #d3d3d3;">
</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var x = 10;
var y = 10;
document.addEventListener('keydown', function(event)
{
if(event.keyCode == 37)//left
{
x -= 100;
console.log("x:"+ x);
}
else if(event.keyCode == 38)//up
{
y -= 100;
console.log("y:"+ y);
}
else if(event.keyCode == 39)//right
{
x += 100;
console.log("x:"+ x);
}
else if(event.keyCode == 40)//down
{
y += 100;
console.log("y:"+ y);
}
draw();
});
function draw(){
ctx.clearRect(0, 0, c.width, c.height);
// grey rectangle
ctx.beginPath();
ctx.lineWidth = "2";
ctx.strokeStyle = "grey";
ctx.rect(5, 5, 400, 400);
ctx.stroke();
// blue rectangle
ctx.beginPath();
ctx.lineWidth = "5";
ctx.strokeStyle = "blue";
ctx.rect(x, y, 100, 100);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
}
draw();
</script>
</body>
</html>
I am attempting to animate an emoticon that was previously drawn in canvas. I am attempting to do a draw and clear using frames following a tutorial but am not getting results. I have 6 frames of the emoticon coded and am unsure how to include this within the code. This is what I have so far:
<!DOCTYPE html>
<html>
<head>
<title>Adding Animation</title>
<style>
canvas {
border: 3px #CCC solid;
}
</style>
</head>
<body>
<div id="container">
<canvas id="myCanvas" height="1200" width="900"></canvas>
</div>
<script>
var mainCanvas = document.querySelector("#myCanvas");
var mainContext = mainCanvas.getContext("2d");
var canvasWidth = mainCanvas.width;
var canvasHeight = mainCanvas.height;
function drawCircle() {
mainContext.clearRect(0, 0, canvasWidth, canvasHeight);
// color in the background
mainContext.fillStyle = "#EEEEEE";
mainContext.fillRect(0, 0, canvasWidth, canvasHeight);
// draw the circle
ctx.beginPath();
ctx.strokeStyle = "000000";
ctx.lineWidth = 5;
ctx.fillStyle = "yellow";
ctx.arc(600, 450, 150, 0, Math.PI * 2, true);
ctx.stroke();
ctx.closePath();
ctx.fill();
//The smile
ctx.beginPath();
ctxstrokeStyle = "black";
ctx.lineWidth = 2;
ctx.arc(600, 475, 75, .1 * Math.PI, Math.PI * .9, false)
ctx.stroke();
ctx.closePath();
//The eyes
//Left
ctx.save();
ctx.scale(0.65, 1);
ctx.beginPath();
ctx.arc(850, 405, 40, 0 * Math.PI, Math.PI * 2, false);
ctx.fillStyle="black";
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
//Right
ctx.save();
ctx.scale(0.65, 1);
ctx.beginPath();
ctx.arc(1000,405,40, 0*Math.PI, Math.PI*2, false);
ctx.fillStyle="black";
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore()
}
drawCircle();
</script>
</body>
</html>
I am unsure if I am even on the right track as I have a difficult time with animation. Does anyone have any suggestions they can give me guidance on?
You have 2 names for the context: mainContext & ctx.
Change it to a single name and your face is "smiley" ! :-)
...
To animate:
Use a requestAnimationFrame loop to change the scaleY value in scale(scaleX,scaleY) over time.
Here's annotated code and a Demo:
var mainCanvas = document.querySelector("#myCanvas");
var ctx = mainCanvas.getContext("2d");
var canvasWidth = mainCanvas.width;
var canvasHeight = mainCanvas.height;
ctx.translate(-425,-275);
drawCircle(1);
// global var to hold pct the left eye is open
// 1==fully open, 0==fully closed
var scaley=1;
var direction=-1;
// request 1 animate() loop
requestAnimationFrame(animate);
function animate(time){
// draw smiley with the specified eye openness
drawCircle(scaley);
scaley+=.02*direction;
if(scaley<0){
scaley=0;
direction=1;
}
if(scaley>1){
scaley=1;
direction=-1;
}
requestAnimationFrame(animate);
}
function drawCircle(scaleY) {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
// color in the background
ctx.fillStyle = "#EEEEEE";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// draw the circle
ctx.beginPath();
ctx.strokeStyle = "000000";
ctx.lineWidth = 5;
ctx.fillStyle = "yellow";
ctx.arc(600, 450, 150, 0, Math.PI * 2, true);
ctx.stroke();
ctx.closePath();
ctx.fill();
//The smile
ctx.beginPath();
ctxstrokeStyle = "black";
ctx.lineWidth = 2;
ctx.arc(600, 475, 75, .1 * Math.PI, Math.PI * .9, false)
ctx.stroke();
//The eyes
//Left
ctx.save();
// move the [0,0] origin to the left eye's centerpoint
ctx.translate(550,405);
// close the left eye by the specified scaleY
ctx.scale(0.65, scaleY);
ctx.beginPath();
// draw the left eye (arc) at 0,0 because
// we translated the origin to [550,405] earlier
ctx.arc(0, 0, 40, 0 * Math.PI, Math.PI * 2, false);
ctx.fillStyle="black";
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
//Right
ctx.save();
ctx.scale(0.65, 1);
ctx.beginPath();
ctx.arc(1000,405,40, 0*Math.PI, Math.PI*2, false);
ctx.fillStyle="black";
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore()
}
<canvas id="myCanvas" height="1200" width="900"></canvas>
You never declared a ctx variable.
Change all your mainContext by ctx and it should be working fine.