Build Sketchy
You will build a drawing pad, using events. draw.js is
ready-made: it gives you drawLine,
clearCanvas, fitCanvas, and
saveDrawing.
See the finished page to know where you are headed, and the lecture if a step uses something you want to review.
Setup
- Open the starter
files on GitHub:
index.html,practice.js, anddraw.js. - Make a new folder in WebStorm. For each file, click Copy raw file, the copy icon above the code, and paste it into a new file with the same name.
- Open
index.htmlin the browser, and reload the page after each step.
practice.js has a banner comment for each section below.
Commented-out page lines in a section are there for you to use:
uncomment them where your code needs them.
Step 1: Brush Size
Fill in showBrush(), then have the size slider and the
Color picker call it on each input.
showBrush has no () in the listener, because
we pass the function and let the browser call it. The section should end
up like this:
function showBrush() {
const size = document.getElementById("size").value;
const color = document.getElementById("color").value;
document.getElementById("size-value").textContent = `${size}px`;
document.getElementById("brush").style.width = `${size}px`;
document.getElementById("brush").style.height = `${size}px`;
document.getElementById("brush").style.backgroundColor = color;
}
showBrush();
document.getElementById("size").addEventListener("input", showBrush);
document.getElementById("color").addEventListener("input", showBrush);Step 2: Draw
Drawing starts on mousedown, and each
mousemove draws a line from the last spot to the mouse.
function startDrawing(event) {
drawing = true;
lastX = event.offsetX;
lastY = event.offsetY;
}
function keepDrawing(event) {
if (drawing) {
drawLine(lastX, lastY, event.offsetX, event.offsetY);
lastX = event.offsetX;
lastY = event.offsetY;
unsaved = true;
}
}
canvas.addEventListener("mousedown", startDrawing);
canvas.addEventListener("mousemove", keepDrawing);Step 3: Stop Drawing
Listen on window, so letting go anywhere stops drawing,
even off the canvas.
window.addEventListener("mouseup", () => {
drawing = false;
});Step 4: Brush Preview
The brush follows the mouse, and shows only while the mouse is over the canvas.
canvas.addEventListener("mousemove", (event) => {
document.getElementById("brush").style.left = `${event.offsetX}px`;
document.getElementById("brush").style.top = `${event.offsetY}px`;
});
canvas.addEventListener("mouseenter", () => {
document.getElementById("brush").hidden = false;
});
canvas.addEventListener("mouseleave", () => {
document.getElementById("brush").hidden = true;
});Step 5: Palette
Clicking a swatch picks its color. Setting value from
code does not fire input, so call showBrush()
yourself.
for (let swatch of document.querySelectorAll(".swatch")) {
swatch.addEventListener("click", (event) => {
document.getElementById("color").value = event.target.value;
showBrush();
});
}Step 6: Shortcuts
Inside useShortcut, c clears the canvas and
1 to 5 pick a color. Skip keys typed in a box,
or typing cat in Title clears the
drawing.
if (event.target.nodeName === "INPUT") {
return;
}
if (event.key === "c") {
clearCanvas();
} else if ("12345".includes(event.key)) {
document.getElementById("color").value = colors[Number(event.key) - 1];
showBrush();
}Step 7: Brush Keys
Also inside useShortcut, [ makes the brush
smaller and ] makes it bigger.
const size = Number(document.getElementById("size").value);
if (event.key === "[") {
document.getElementById("size").value = size - 2;
showBrush();
} else if (event.key === "]") {
document.getElementById("size").value = size + 2;
showBrush();
}Under the function, have document call it on each
keydown:
document.addEventListener("keydown", useShortcut);Step 8: Fit the Window
fitCanvas() makes the canvas match the page again after
the window changes size.
window.addEventListener("resize", fitCanvas);Step 9: Unsaved Drawing
preventDefault() on beforeunload makes the
browser ask before leaving. Draw something, then reload to test it.
window.addEventListener("beforeunload", (event) => {
if (unsaved) {
event.preventDefault();
}
});Step 10: Save
Save is the form's submit button. Stop the reload, then download the drawing.
document.getElementById("tools").addEventListener("submit", (event) => {
event.preventDefault();
const name = fileName();
saveDrawing(name);
document.getElementById("message").textContent = `Saved ${name}`;
unsaved = false;
});Step 11: Title Hint
The hint shows only while the Title box has focus.
document.getElementById("title").addEventListener("focus", () => {
document.getElementById("hint").hidden = false;
});
document.getElementById("title").addEventListener("blur", () => {
document.getElementById("hint").hidden = true;
});Step 12: Mouse Position
Show where the mouse is on the canvas.
function showPosition(event) {
const x = Math.round(event.offsetX);
const y = Math.round(event.offsetY);
document.getElementById("where").textContent = `${x}, ${y}`;
}
canvas.addEventListener("mousemove", showPosition);Compare
Your page should now match the finished page. If something differs, compare your files with the final code on GitHub.
Extras
Pick any of these once the page works.
Eraser
At the top of practice.js, add
let inkColor = colors[0];. Then, in
useShortcut:
if (event.key === "e") {
const color = document.getElementById("color");
if (color.value === "#ffffff") {
color.value = inkColor;
} else {
inkColor = color.value;
color.value = "#ffffff";
}
showBrush();
}The canvas is white, so drawing in white erases.
Wheel Size
canvas.addEventListener("wheel", (event) => {
event.preventDefault();
const size = Number(document.getElementById("size").value);
if (event.deltaY < 0) {
document.getElementById("size").value = size + 2;
} else {
document.getElementById("size").value = size - 2;
}
showBrush();
});deltaY is below 0 when the wheel scrolls up.
preventDefault() keeps the page from scrolling while the
mouse is over the canvas.
Touch
| Mouse event | Pointer event |
|---|---|
mousedown |
pointerdown |
mousemove |
pointermove |
mouseup |
pointerup |
mouseenter |
pointerenter |
mouseleave |
pointerleave |
Pointer events come from a mouse, a pen, or a
finger, with the same offsetX and offsetY.
Swap each mouse event for its pointer event, and add
touch-action: none; to #canvas, so a finger
draws instead of scrolling the page.