How to Create a Simple Paint Tool with Canvas API

By
coding
javascript
canvas
Open in Demo.js

Online drawing tools are very popular due to their no-installation requirement, lightweight nature, and accessibility from anywhere. The JavaScript Canvas API provides a range of features for developing this type of online drawing app, from basic to advanced. In this article, we will explore how to develop a simple drawing app with a pencil tool using the Canvas API.

Look at the following example. Select a color and drag on the canvas space to start drawing.

First, we need to create a canvas element. Provide a specific width and height.

<canvas width="240" height="180"></canvas>

Then add the following CSS to the canvas. Avoiding touch actions is very important for mobile devices; otherwise, dragging on the canvas will start to scroll the web page. Also, you can set a background color and a suitable cursor for a better user experience.

canvas {
  /* avoid mobile device scrolling */
  touch-action: none;
  /* default background color */
  background-color: #fff;
  /* cursor to display */
  cursor: crosshair;
}

In JavaScript, you can get the canvas element and its 2D context. This context provides all the rendering interface to our canvas, such as drawing shapes, images, reading pixel data, and clearing content.

// get canvas element
const canvas = document.querySelector("#canvas")
// get canvas context
const context = canvas.getContext("2d")

Then define the global data states and values that are required during the drawing. Since JavaScript has no direct way of detecting the pointer drag event, first we detect the pointer down action and remember that the pointer is down in a boolean variable. While listening to the pointer move action, we check if that boolean value is set to true and determine if the cursor is dragging or not. When the pointer is up or exits the canvas, we can set the boolean back to false.

// drawing active state
let isActive = false

// set as active on pointer down
canvas.addEventListener("pointerdown", () => isActive = true)

// listen to pointer move event
canvas.addEventListener("pointermove", event => {
  // return if not active
  if (!isActive) { return }
  // HERE: user is drawing
})

// set as inactive on pointer out
canvas.addEventListener("pointerout", () => isActive = false)

// set as inactive on pointer up
canvas.addEventListener("pointerup", () => isActive = false)

While dragging on the canvas, we are going to draw lines for each cursor move point. To do this, we need the previous cursor point and the current cursor point. Therefore, while the pointer is setting down, we have to remember that point as the line's starting position.

// last drawn coordinates
let lastX = 0
let lastY = 0

// listen to pointer down event
canvas.addEventListener("pointerdown", event => {
  // set as active
  isActive = true
  // remember as starting point
  lastX = event.offsetX
  lastY = event.offsetY
})

Now we can use that starting point and the current point to draw the line during dragging. Here you can set a color to the strokeStyle before drawing the line. And at the end of line drawing, store the current point as the last point so each line user draws will be connected as a drawing path while dragging on the canvas.

// listen to pointer move event
canvas.addEventListener("pointermove", event => {
  // return if not active
  if (!isActive) { return }
  // set a line color
  context.strokeStyle = "red"
  // set drawing style
  context.lineWidth = 2
  context.lineCap = "round"
  // get current pointer coordinates
  const x = event.offsetX
  const y = event.offsetY
  // draw line
  context.beginPath()
  context.moveTo(lastX, lastY)
  context.lineTo(x, y)
  context.stroke()
  // remember last coordinates
  lastX = x
  lastY = y
})

To clear the entire canvas, you can use the following method and provide the exact canvas size. You can bind this with a button click with a user confirmation, so the canvas will not be cleared without user intention.

// clear canvas by its dimensions
context.clearRect(0, 0, canvas.width, canvas.height)