I am creating this post as notes from Wes Bos Javascript course which you can sign up for and do with me here: https://beginnerjavascript.com/. Here is a link to Wes' GitHub readme.md.
Now that we have covered some of the basics with DOM manipulation, Scope, Closures, Hoisting, and Events - we are going to get into some of the fun stuff. Let's build a fun Etch-A-Sketch module. This article follows along with Wes Bos' course Beginner JavaScript on module 6, slide 33. Let's get into it!
Getting Started
Turn on some tunes, I recommend this Google music station. Get your Visual Studio Code editor ready. I recommend a couple of extensions to make this a bit easier:
- Prettier
- Live Server
- indent-rainbow
- Rainbow Brackets
If you haven't read up on my DOM Manipulation or Events articles yet, everything covered in this guide relies heavily on knowledge dropped there.
First, you need some HTML. You can nab that from Wes' Github Repo here. It contains the beginnings and the final project. Things in that repo change between when this was written to now and I wanted to give you a reference in case you need to see the updated `finished` javascript files. Here is the HTML
<!DOCTYPE html5> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Etch A Sketch</title> </head> <body> <div class="canvasWrap"> <canvas width="1600" height="1000" id="etch-a-sketch"></canvas> <div class="buttons"> <button class="shake">Shake!</button> </div> </div> <style> body { min-height: 100vh; display: grid; align-items: center; justify-items: center; background: white; background: url(https://s3.amazonaws.com/media.locally.net/original/HABA_ALT_2017-08-02-13-22-45.jpg); background-size: cover; } canvas { border: 30px solid #e80000; border-radius: 10px; /* Set the width and height to half the actual size so it doesn't look pixelated */ width: 800px; height: 500px; background: white; } canvas.shake { animation: shake 0.5s linear 1; } @keyframes shake { 10%, 90% { transform: translate3d(-1px, 0, 0); } 20%, 80% { transform: translate3d(2px, 0, 0); } 30%, 50%, 70% { transform: translate3d(-4px, 0, 0); } 40%, 60% { transform: translate3d(4px, 0, 0); } } </style> </body> </html>
Click on theHTML
file in Visual studio code and 2 finger/right click to "Open with Live Server" - should look something like this:
Create Some Javascript!
Create a Javascript file named etch-a-sketch.js in the same directory as the index.html.
Add a script tag to your Javascript file, just above the </body>
tag like this:
<script type="text/javascript" src="etch-a-sketch.js"></script>
I spent a whole lot of time trying to figure out why the 2 files weren't connecting. Joke is on me, I mis-spelled "javascript" in the type section above and couldn't understand for the life of me why I couldn't connect. Get your Lolz. Also, It took me entirely too long to realize I was calling the script in the
Drewhead
of the HTML document before thecanvas
element is painted rendering my selector of it to be null. Don't do that, I touch on why this happens here.
Test that the files are connected by creating a console.log("Connected!");
on the etch-a-sketch.js file. Save and then reload the page. Now take a peek at the console log to ensure it's working. You should see something like this:
Sudo Code
Sudo code is kind of how you can outline the things that need to be written in code. Below you will see the main objectives of the code we are going to write. You will see this in action more as we go. Go ahead and put this in your .js file.
// SELECT THE ELEMENTS ON THE PAGE -CanvasGradient, SHAKE GamepadButton // SETUP OUR CANVAS FOR DRAWING // WRITE DRAW Function // WRITE A HANDLER FOR THE ARROW KEYS // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS
Canvas
Sizing
We will learn about canvas
a bit more as this article continues since we haven't really discussed it much yet but for now just know that it's an HTML element used for Javascript to draw on. It's in the HTML above and looks like this currently:
<canvas width="1600" height="1000" id="etch-a-sketch"></canvas>
What the line above says how large the canvas should be, but you will see in the style
tags there is a canvas CSS section that looks like this:
canvas { border: 30px solid #e80000; border-radius: 10px; /* Set the width and height to half the actual size so it doesn't look pixelated */ width: 800px; height: 500px; background: white; }
This changes the way that 1600px by 1000px canvas is displayed but doesn't change the positioning within the canvas
for Javascript purposes. This is done so it doesn't look pixelated. To avoid pixelation, you want the canvas size at least double the canvas screen size to increase the resolution.
How To Use canvas
This is the element: <canvas width="1600" height="1000" id="etch-a-sketch"></canvas>
Note the id
If you haven't read up on my DOM Manipulation or Events articles yet, now would be a good time because now we are going to select elements and create events on it.
The way that it works is that you select the canvas, then the canvas' context (either 2D or 3D, we will be using 2D today) and then you use a set of methods that are used for drawing to the canvas - Things like paint, draw, circle, etc (think MS Paint).
Let's start by establishing our const
s, we need one per our 'sudo code' for canvase, shake, and the canvas' context
as described above. Should look something like this:
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); console.log(canvas); // should not be null, if it is -- see my note in the Create JS section const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); // SETUP OUR CANVAS FOR DRAWING ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; // WRITE DRAW Function // WRITE A HANDLER FOR THE ARROW KEYS // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS
In the Javascript console we can refresh the page and run ctx
and see the properties of it.
I know I didn't cover it too deeply but I added
ctx.lineJoin
andctx.lineCap
and set them toround
because by default these are squared meaning it could make it look pixelated and not a very polished final product. I also added ctx.lineWidth = 10; (you don't have to specify px here)
Let's draw something on the page just so we can see something and know it's all working. We will start with a specific point and then we can randomize that later.
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); console.log(canvas); // should not be null, if it is -- see my note in the Create JS section const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); // SETUP OUR CANVAS FOR DRAWING ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(200, 200); ctx.lineTo(200, 200); ctx.stroke(); // WRITE DRAW Function // WRITE A HANDLER FOR THE ARROW KEYS // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS
Let's break down the code above. Think of .beginPath();
as a way to specify where we start the drawing. Like picking a point where you put your pen down on a piece of plot paper.
We then select an (x,y) corordinates in pixels (again, you do not have to specify px for this) for where to start using .moveTo(x,y);
and then a completed coordinate (where you pick the pen up) using .lineto(x,y);
.
The .stroke();
allows us to connect the dots. Currently since they are both the same (x,y) coordinates, they really result in just a circle that is 10px by 10px that is 200px from the top of the canvas and 200px from the left of the canvas. This is the result:
Finding A Random Spot To Start Drawing From
What you can see above is we have a dot. What is really cool about etch-a-sketch is that we can start at a random spot. How could we do that?
We could take the width, the height, and we could select a random number between the height and the width. Let's set that up in Javascript.
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); console.log(canvas); const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); // SETUP OUR CANVAS FOR DRAWING const width = canvas.width; const height = canvas.height; console.log(width, height); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(200, 200); ctx.lineTo(200, 200); ctx.stroke(); // WRITE DRAW Function // WRITE A HANDLER FOR THE ARROW KEYS // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS
The Javascript console will show: "1600 1000" because that is the actual width and height of the canvas as we established in the HTML
on line 11 (not the display width, remember we changed that in CSS).
A Little Restructuring Using Destructuring
We can shorten this using destructuring. Let's look at it and then break that down:
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); console.log(canvas); const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); // SETUP OUR CANVAS FOR DRAWING // -- MAKE A VARIABLE CALLED HEIGHT AND WIDTH FROM THE SAME PROPERTIES ON OUR CANVAS const { width, height} = canvas; console.log(width, height); // -- CREATE RANDOM X AND Y COORDINATES ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(200, 200); ctx.lineTo(200, 200); ctx.stroke(); // WRITE DRAW Function // WRITE A HANDLER FOR THE ARROW KEYS // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS
Destructuring means that we are going to pull the property out of a variable. It prevents us from having to rewrite the same variable we are trying to pull a property out of. The context is like this:var {property1, property2,
etc
..} = varName
Creating Random Numbers
Javascript comes with some built in randomizing tools. Let's take a look at Math.random()
The
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/randomMath.random()
function returns a floating-point (meaning it includes decimals), pseudo-random number in the range 0–1 (inclusive of 0, but not 1) with approximately uniform distribution over that range — which you can then scale to your desired range. The implementation selects the initial seed to the random number generation algorithm; it cannot be chosen or reset by the user. Note this is not a cryptographically secure method of randomizing numbers.
What this means is if we run const randoNum = Math.random()*100;
we will get a number between 0 (including 0) and 100 (but not including 100). So we could potentially have a number of 99.999999999~ but never actually have 100 as a result.
Math.floor();
allows us to round down to the closest whole number (like the floor) and Math.ceiling();
allows us to round up to the closest whole number. So we could use something like: const randoNum = Math.floor(Math.random()*100);
which would round the random number down to the closest whole number.
Applying What We Just Learned: Creating Random X & Y Coordinates
Create a variable for X like this:let x = Math.floor(Math.random()* width);
and then use this cool feature to copy the line down: ⬆⌥?
(shift, option, up/down) to copy that line up or down. /sauce
Change the copied line to:let
y
= Math.floor(Math.random()*
height
);
Now you can swap out the coordinates from (200, 200) in the ctx.moveTo(x, y)
& ctx.moveTo(x, y)
. It should now look something like this in your Javascript file:
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); // SETUP OUR CANVAS FOR DRAWING // -- MAKE A VARIABLE CALLED HEIGHT AND WIDTH FROM THE SAME PROPERTIES ON OUR CANVAS const { width, height} = canvas; // -- CREATE RANDOM X AND Y COORDINATES let x = Math.floor(Math.random()* width); let y = Math.floor(Math.random()* width); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(x, y); ctx.lineTo(x, y); ctx.stroke(); // WRITE DRAW Function // WRITE A HANDLER FOR THE ARROW KEYS // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS
Now every time we refresh the page, we will see that dot move around. (Note, I increased the line width to 50 for this so you don't have to strain to see it in the gif).
Events!
If you haven't read up on my DOM Manipulation or Events articles yet, now would be a good time because now we are going to select the canvas and start selecting it and creating events on it.
Create An Event Handler and Listener.
If we want to listen for a keypress while the screen is loaded but without any other interactions, we can set the eventListener
to listen on the window
.
We will add an eventListener
for a keydown
event. Add the following line of code to your javascript:
window.addEventListener('keydown', handlekey);
Currently when you press an up or down key, we aren't doing anything with it. Let's fix that:
// WRITE A HANDLER FOR THE ARROW KEYS function handlekey () { console.log('HANDLING KEY`); }
Now if we run that and press the ? or ?keys we will see the whole window moves. Why do you think that is? Here is a little hint.
Prevent The Default Key Responses
To prevent that default we will have to pass an event and use the preventDefault
, it should look something like this. (Wes demonstrates using an e
in place of event
here but that just demonstrates that this is a placeholder and you can literally put anything you want in there.
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); // SETUP OUR CANVAS FOR DRAWING // -- MAKE A VARIABLE CALLED HEIGHT AND WIDTH FROM THE SAME PROPERTIES ON OUR CANVAS const { width, height} = canvas; // -- CREATE RANDOM X AND Y COORDINATES let x = Math.floor(Math.random()* width); let y = Math.floor(Math.random()* width); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(x, y); ctx.lineTo(x, y); ctx.stroke(); // WRITE DRAW Function // WRITE A HANDLER FOR THE ARROW KEYS function handlekey (event) { event.preventDefault(); // note you will disable refresh shortcuts console.log('HANDLING KEY`), } // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS window.addEventListener('keydown', handlekey);
Handling Only The Arrow Key
Currently, we are listening for any key press on the whole keyboard. This renders shortcuts like page refreshes Here is where if
statements will come in handy and since we know from typing keys in the window from the above code, the arrow keys all include the word Arrow
in them. Let's look at how this will work.
// WRITE A HANDLER FOR THE ARROW KEYS function handlekey (event) { if(event.key.includes('Arrow'){ event.preventDefault(); console.log('HANDLING KEY`), }); }
You should now be able to use ⌘ r
to refresh the page and it actually work. We now need to hand off the key to the draw
function.
Side note: if you want to add the
Drew⌘
icon on mac to text, it's not called command. It's actually called "place of" and you can pull up emojis on any page/app usingctrl
+⌘
+spacebar
. I still cannot find good one for spacebar or ctrl ( up arrowhead works but it's ugly with my font). Feel free to comment them if you find'em!
Write a Draw Function
We can create a function draw(key){console.log(console.log(key)
but this isn't very good if you had to pass in multiple options. So the best way to do this is to pass in an options object like this:
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); // SETUP OUR CANVAS FOR DRAWING // -- MAKE A VARIABLE CALLED HEIGHT AND WIDTH FROM THE SAME PROPERTIES ON OUR CANVAS const { width, height} = canvas; // -- CREATE RANDOM X AND Y COORDINATES let x = Math.floor(Math.random()* width); let y = Math.floor(Math.random()* width); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(x, y); ctx.lineTo(x, y); ctx.stroke(); // WRITE DRAW Function function draw(option) { console.log(options); } // WRITE A HANDLER FOR THE ARROW KEYS function handlekey (event) { if(event.key.includes('Arrow')){ event.preventDefault(); draw({key: event.key}); }; } // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS window.addEventListener('keydown', handlekey);
We can also destructure function declarations! Let's take a look at that above but instead of passing option
we will pass {key}
and match that in the arguments of the console.log like this:
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); // SETUP OUR CANVAS FOR DRAWING // -- MAKE A VARIABLE CALLED HEIGHT AND WIDTH FROM THE SAME PROPERTIES ON OUR CANVAS const { width, height} = canvas; // -- CREATE RANDOM X AND Y COORDINATES let x = Math.floor(Math.random()* width); let y = Math.floor(Math.random()* width); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(x, y); ctx.lineTo(x, y); ctx.stroke(); // WRITE DRAW Function function draw({key}) { console.log(key); } // WRITE A HANDLER FOR THE ARROW KEYS function handlekey (event) { if(event.key.includes('Arrow')) { event.preventDefault(); draw({key: event.key}); }; } // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS window.addEventListener('keydown', handlekey);
We can take properties and rename them to object variables, this also allows us to create shorter variable names. Now when someone uses their keys we can go ahead and start to draw directly on the canvas.
Draw!
Create Some Movement
We will need to add a new .moveTo()
function and change the x & y values based on what key the user presses. Let's take a look at how this would work:
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); // SETUP OUR CANVAS FOR DRAWING // -- MAKE A VARIABLE CALLED HEIGHT AND WIDTH FROM THE SAME PROPERTIES ON OUR CANVAS const { width, height} = canvas; // -- CREATE RANDOM X AND Y COORDINATES let x = Math.floor(Math.random()* width); let y = Math.floor(Math.random()* width); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(x, y); ctx.lineTo(x, y); ctx.stroke(); // WRITE DRAW Function function draw({key}) { console.log(key); //START THE PATH ctx.beginPath(); //FROM THE RANDOM STARTING POINT ctx.moveTo(x, y); // MOVE TO NEW COORDINATES //MOVE OUR X & Y VALUES DEPENDING ON WHAT THE USER PRESSED x -= 10; // DECREASE X VALUE BY 10PX y -= 10; // DECREASE Y VALUE BY 10PX ctx.lineTo(x,y); // CONNECTS THE 2 POINTS ctx.stroke(); // DRAWS THE LINE ALONG THE CONNECTION BETWEEN 2 POINTS ABOVE }; // WRITE A HANDLER FOR THE ARROW KEYS function handlekey (event) { if(event.key.includes('Arrow')) { event.preventDefault(); draw({key: event.key}); }; } // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS window.addEventListener('keydown', handlekey);
What you should now see is:
Regardless of the key pressed the dot moves left 10px and up 10 px. Let's address that!
Change The Direction Based On Key Stroke (Flow Control)
Obviously the above isn't what we are looking for because we hardcoded values in the draw function. Let's address that first. At the top of the file let's create a const for the amount each keypress moves by.
Switch Statements
A user may go up
down
right
or left
. Switch Statements are a perfect use case, which are basically saying "We have four possible cases, based on these do the following". You need to add a break
at the end of each case and provide a default
which says basically if none of these cases happen, what do you do. In this case, we don't want it to do anything so we just break;
on it.
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); const moveAmmount = 10; // SETUP OUR CANVAS FOR DRAWING // -- MAKE A VARIABLE CALLED HEIGHT AND WIDTH FROM THE SAME PROPERTIES ON OUR CANVAS const { width, height} = canvas; // -- CREATE RANDOM X AND Y COORDINATES let x = Math.floor(Math.random()* width); let y = Math.floor(Math.random()* width); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(x, y); ctx.lineTo(x, y); ctx.stroke(); // WRITE DRAW Function function draw({key}) { console.log(key); //START THE PATH ctx.beginPath(); ctx.moveTo(x, y); //MOVE OUR X & Y VALUES DEPENDING ON WHAT THE USER PRESSED switch(key){ default: break; case 'ArrowUp': y -= moveAmmount; break; case 'ArrowRight': x += moveAmmount; break; case 'ArrowDown': y += moveAmmount; break; case 'ArrowLeft': x -= moveAmmount; break; } ctx.lineTo(x,y); ctx.stroke(); }; // WRITE A HANDLER FOR THE ARROW KEYS function handlekey (event) { if(event.key.includes('Arrow')) { event.preventDefault(); draw({key: event.key}); }; } // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS window.addEventListener('keydown', handlekey);
While I said you could make an Etch A Sketch, I didn't say I could use it. You should be able to use the code above to create something like this now:
Change The Colors
Check out mother-effinghsl.com - you can set the hsl value, which updates the hue value by 1 which will give us a little rainbow line. We will do that by changing the first number in ctx.strokeStyle
=
hsl(100, 100%, 50%
and use our fancy );
${}
tool to call that hue variable (remember this only works with backticks!).
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); const moveAmmount = 10; let hue = 0; ctx.strokeStyle = `hsl(${hue}, 100%, 50%)` //GET THIS FROM MOTHER-EFFINGHSL.COM // SETUP OUR CANVAS FOR DRAWING // -- MAKE A VARIABLE CALLED HEIGHT AND WIDTH FROM THE SAME PROPERTIES ON OUR CANVAS const { width, height} = canvas; // -- CREATE RANDOM X AND Y COORDINATES let x = Math.floor(Math.random()* width); let y = Math.floor(Math.random()* width); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(x, y); ctx.lineTo(x, y); ctx.stroke(); // WRITE DRAW Function function draw({key}) { //INCREMENT THE HUE hue += 10 ctx.strokeStyle = `hsl(${hue}, 100%, 50%)` //GET THIS FROM MOTHER-EFFINGHSL.COM //START THE PATH ctx.beginPath(); ctx.moveTo(x, y); //MOVE OUR X & Y VALUES DEPENDING ON WHAT THE USER PRESSED switch(key){ default: break; case 'ArrowUp': y -= moveAmmount; break; case 'ArrowRight': x += moveAmmount; break; case 'ArrowDown': y += moveAmmount; break; case 'ArrowLeft': x -= moveAmmount; break; } ctx.lineTo(x,y); ctx.stroke(); }; // WRITE A HANDLER FOR THE ARROW KEYS function handlekey (event) { if(event.key.includes('Arrow')) { event.preventDefault(); draw({key: event.key}); }; } // CLEAR "SHAKE" Function // LISTEN FOR ARROW KEYS window.addEventListener('keydown', handlekey);
The Shake!
CSS class of .shake
has an animation of shake for half a second that will run one time. There are some animation translations in the CSS as well. If you click the shake button - it'll run and attach that class. If you tried to run it again, the class will fail because the class is already applied.
// CLEAR "SHAKE" Function function clearCanvas () { canvas.classList.add('shake'); }
So to fix that, we need to create an event listener that listens to when the animation ends in CSS.
shakeButton.addEventListener('click', clearCanvas);
Now let's put it all together:
// SELECT THE ELEMENTS ON THE PAGE -Canvas, SHAKE const canvas = document.querySelector('#etch-a-sketch'); const ctx = canvas.getContext('2d'); const shakeButton = document.querySelector(`.shake`); const moveAmmount = 10; let hue = 0; ctx.strokeStyle = `hsl(${hue}, 100%, 50%)` //GET THIS FROM MOTHER-EFFINGHSL.COM // SETUP OUR CANVAS FOR DRAWING // -- MAKE A VARIABLE CALLED HEIGHT AND WIDTH FROM THE SAME PROPERTIES ON OUR CANVAS const { width, height} = canvas; // -- CREATE RANDOM X AND Y COORDINATES let x = Math.floor(Math.random()* width); let y = Math.floor(Math.random()* width); ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.lineWidth = 10; ctx.beginPath(); //STARTS THE DRAWING (PUTS PEN ON PAPER) ctx.moveTo(x, y); ctx.lineTo(x, y); ctx.stroke(); // WRITE DRAW Function function draw({key}) { //INCREMENT THE HUE hue += 10 ctx.strokeStyle = `hsl(${hue}, 100%, 50%)` //GET THIS FROM MOTHER-EFFINGHSL.COM //START THE PATH ctx.beginPath(); ctx.moveTo(x, y); //MOVE OUR X & Y VALUES DEPENDING ON WHAT THE USER PRESSED switch(key){ default: break; case 'ArrowUp': y -= moveAmmount; break; case 'ArrowRight': x += moveAmmount; break; case 'ArrowDown': y += moveAmmount; break; case 'ArrowLeft': x -= moveAmmount; break; } ctx.lineTo(x,y); ctx.stroke(); }; // WRITE A HANDLER FOR THE ARROW KEYS function handlekey (event) { if(event.key.includes('Arrow')) { event.preventDefault(); draw({key: event.key}); }; } // CLEAR "SHAKE" Function function clearCanvas () { canvas.classList.add('shake'); ctx.clearRect(0, 0, width, height); console.log(`done the shake`); canvas.addEventListener('animationend', ()=> { canvas.classList.remove('shake'); }, {once: true} // auto removes the listener when it's done ); } // LISTEN FOR ARROW KEYS window.addEventListener('keydown', handlekey); shakeButton.addEventListener('click', clearCanvas);
All done! You just made an etch-a-sketch you can show off and brag to your friends about!
My Ask and Final Thoughts
We put together all the things we learned in my last few articles and you can play around with my final version of this project here.
If you found this article helpful, share/retweet it and follow me on twitter @codingwithdrewk! There is so much more in Wes' courses I think you will find valuable as I have. I'm learning so much and really enjoying the course, Wes has an amazingly simple way to explain the difficult bits of Javascript that other courses I've taken could only wish. You can view the course over at WesBos.com. (I am in no way getting any referrals or kickbacks for recommending this)
Drew is a seasoned DevOps Engineer with a rich background that spans multiple industries and technologies. With foundational training as a Nuclear Engineer in the US Navy, Drew brings a meticulous approach to operational efficiency and reliability. His expertise lies in cloud migration strategies, CI/CD automation, and Kubernetes orchestration. Known for a keen focus on facts and correctness, Drew is proficient in a range of programming languages including Bash and JavaScript. His diverse experiences, from serving in the military to working in the corporate world, have equipped him with a comprehensive worldview and a knack for creative problem-solving. Drew advocates for streamlined, fact-based approaches in both code and business, making him a reliable authority in the tech industry.