Live Coding: Timeouts, Debugging, and Packages
Timing in JavaScript
There are a few functions in JavaScript which deal with timing the calling of functions:
// calls function after duration milliseconds from now
setTimeout(function, duration);
// calls function after every duration milliseconds
setInterval(function, duration);
// calls a callback function at a frequency matching the display refresh rate
requestAnimationFrame(callback);
These can be using in conjunction with CSS styles on document object model elements to create animations.
Here's an example using setTimeout()
:
let cx = document.querySelector("canvas").getContext("2d");
setTimeout(function() {
getEmojis("https://api.github.com/emojis")
.then(emojiData => {
for(let i=0; i<25; i++){
let faceImg = new Image();
faceImg.src = emojiData.smiling_face_with_three_hearts;
faceImg.onload = function(){
let locX = Math.round(Math.random() * (736 - 64) + 64);
let locY = Math.round(Math.random() * (536 - 64) + 64);
faceImg.style.left = locX;
faceImg.style.top = locY;
cx.drawImage(faceImg, locX, locY, 64, 64);
}
}
})
}, 3000);
async function getEmojis(file) {
let myObject = await fetch(file);
let myJSobj = await myObject.json();
return myJSobj;
}
Here's an example using setInterval()
to create trails:
let cx = document.querySelector("canvas").getContext("2d");
let locs = [];
getEmojis("https://api.github.com/emojis")
.then(emojiData => {
for(let i=0; i<25; i++){
let locX = Math.round(Math.random() * (736 - 64) + 64);
let locY = Math.round(Math.random() * (536 - 64) + 64);
locs.push([locX, locY]);
}
for(let i=0; i<25; i++){
let newFace = new Emoji(locs[i][0], locs[i][1], emojiData.smiling_face_with_three_hearts);
newFace.faceImg.onload = function(){
setInterval(function() {
newFace.update();
newFace.display();
cx.drawImage(newFace.faceImg, newFace.locX, newFace.locY, 64, 64);
}, 120);
}
}
});
async function getEmojis(file) {
let myObject = await fetch(file);
let myJSobj = await myObject.json();
return myJSobj;
}
class Emoji{
constructor(locX, locY, source){
this.faceImg = new Image();
this.faceImg.src = source;
this.locX = locX;
this.locY = locY;
this.stepX = Math.random() * 5;
this.stepY = Math.random() * 5;
}
update(){
this.locX += this.stepX;
if(this.locX>736){
this.locX = 64;
}
this.locY += this.stepY;
if(this.locY>536){
this.locY = 64;
}
}
display(){
this.faceImg.style.left = this.locX;
this.faceImg.style.top = this.locY;
}
}
requestAnimationFrame()
allows you to execute code on the next available screen repaint, syncing with the user’s browser and hardware to make changes to the screen:
let raindrops = [];
let canvas = document.querySelector("canvas");
canvas.width = window.innerWidth-20;
canvas.height = window.innerHeight-20;
let cx = canvas.getContext("2d");
let stopAnim;
class rainDroplet {
constructor() {
this.initX = Math.random() * canvas.width;
this.initY = Math.random() * canvas.height;
this.speed = Math.random() * 8;
//this.colour = `rgb(${Math.floor(Math.random()*255)} ${Math.floor(Math.random()*255)} ${Math.floor(Math.random()*255)})`;
}
drawRose(){
cx.moveTo(this.initX, this.initY);
cx.beginPath();
for (let i = 0; i < (Math.PI * 2); i += 0.1) {
let r = 20 * Math.abs(Math.cos(4 * i));
let x = (r * Math.cos(i)) + this.initX;
let y = (r * Math.sin(i)) + this.initY;
cx.lineTo(x, y/*, x1, y1*/);
cx.stroke();
cx.strokeStyle = "pink";
//cx.strokeStyle = this.colour;
}
}
moveRose(){
this.initY += this.speed;
if(this.initY > canvas.height + 20){
this.initY = -this.initY;
}
}
}
function initialise(){
for(let i=0; i<100; i++){
let raindrop = new rainDroplet();
raindrops.push(raindrop);
}
}
function draw(){
for(let i = 0; i < raindrops.length; i++){
raindrops[i].drawRose();
}
}
function update(){
for(let i = 0; i < raindrops.length; i++){
cx.clearRect(0, 0, canvas.width, canvas.height);
raindrops[i].moveRose();
//console.log(raindrops[i].initY + " and " + raindrops[i].initX);
}
draw();
window.requestAnimationFrame(update);
}
initialise();
update();
Debugging with console.log
For all debugging, you need two things:
🗺️ a map of the path taken through the program
🔦 a torch to shine a light where JavaScript went astray
For the map, you can use your knowledge of the order in which the program runs. If you’re not sure, start at the very beginning and work it out bit by bit.
For the torch, you have several options:
Error messages (syntax, runtime, logical) provided in the console when the program runs
console.log
statements placed at possible wrong turnsThe
debugger
statement and related tools in editors and browsers
Here’s a buggy program to practice on:
<script src="app.js" defer></script>
<!doctype html>
<html lang="en-GB">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sandbox: Debugging</title>
<link rel="stylesheet" href="styles.css" />
<!-- #region script -->
<script src="app.js" defer></script>
<!-- #endregion script -->
</head>
<body>
<main class="container">
<h1>Sandbox: Debugging</h1>
<!-- #region buttons -->
<div id="button-wrapper">
<button value="mustard">Mustard</button>
<button value="red">Red</button>
<button value="blue">Blue</button>
</div>
<section class="flex-container">
<div class="square mustard"></div>
</section>
<!-- #endregion buttons -->
</main>
</body>
</html>
let buttonWrapper = document.querySelectorAll('#button-wrapper')
function changeColor(event) {}
let color = event.value
let square = document.querySelector('.square')
square.className = `square ${color}`
square.innerHTML = `<div class="color-label">${color}</div>`
}
buttonWrapper.addEventListener('click', changeColor)
Installing an NPM package
To add a package like chalk to your project, run this at the command line:
npm install chalk
This will automatically create or update the package.json
file with the appropriate line in the dependencies
array:
{
"type": "module",
"dependencies": {
"chalk": "^5.3.0"
}
}
TIP
You may need to add "type": "module",
manually.
Then import the package according to its documentation in your JavaScript:
import chalk from 'chalk'
console.log(chalk.green('Green!'))
Telling Git to ignore source code from NPM packages
In your .gitignore
file:
node_modules