Flood Fill and Line Tool for HTML Canvas

Flood Fill is a common tool for drawing apps, but HTML Canvas doesn’t have a built-in flood fill function. And although there is a built-in function for drawing a straight line with lineTo, it’s a bit lacking for customization, so we’re going to go over how to make both a flood fill function as well as a line tool. I’ve built these functions to go along with an undo button infrastructure, so these functions can be used for more advanced drawing apps.

Click the color swatch to change colors

The context for these tools is that I’m using an offscreen canvas as the true drawing surface and rendering it to an onscreen canvas as we draw. This is helpful to keep pixel sizes consistent while allowing for smooth scaling of the canvas and temporary graphics such as cursors, zooming, and a line preview for the line tool.

For every mouse event, we use this code to first get the mouse coordinates as it relates to the offscreen canvas, since we’re clicking on the onscreen canvas:

Flood Fill

Before we look at the function itself, let’s go over when it gets activated and how it relates to the undo stack. For the standard draw function, I’m storing all the points that we draw as the mouse position changes on the mousemove event and pushing that whole array as one action to the undostack on the mouseup event.

For a flood fill, we only need to store one point, the origin point, where the user clicked. So for flood fill, we only need to use the mousedown event to run the function, and the mouseup event to push the action to the undo stack.

If the tool type is “fill”, run this in the mousedown event:

Push to undo stack on mouseup:

Now the actual fill function. As you can see, we’re passing in three arguments, the coordinates plus the color. It’s important to pass in color rather than just use the current brush color, because for the undo/redo function we need to be able to run this function again with whatever color was used for past actions.

We’ll be manipulating the color data itself rather than filling pixels, so first we need to get the imagedata, along with the index of the pixel we clicked on and its color.

If the color clicked is the same as the current brush color we passed in, exit here.

The next bit of code I adopted from William Malone’s code for a floodfill function:

Line Tool

This tool is a bit different in that you need to generate a preview before actually rendering the line, so the artist can see what their line will look like before actually drawing it. On the mousedown event, all we need is to set the starting point of the line:

On mouseup, we can use the origin point and the current point to draw a line, and we’ll push both points to the undostack:

As you can see, this time when we call the action, we’re also passing in the context. This is because when the mouse moves before letting the mouse up, we need to generate a preview which we’ll draw only on the onscreen canvas. In the mousemove event, we need to gather the onscreen coordinates. ocwidth is the onscreen canvas width:

All that’s really doing is creating appropriately rounded coordinates according to the scale difference between the onscreen and offscreen canvas. We’ll also keep track of the last onscreen coordinates, and only render the preview line when the coordinates change.

Not only are we passing in the onscreen context now, but we have an additional argument, which is the scale difference between contexts. Let’s look at the line function:

The parameter scale has a default value of 1, so when this is called for the offscreen canvas, it draws the pixels at original size. For the onscreen preview, we passed in the arguments for the offscreen endpoints, not the onscreen endpoints. This way, we can just scale it up appropriately and efficiently generate a preview line instead of trying to calculate how to draw a matching pixelated line using a much bigger line length. Finally, we have to draw the last point on the line separately, to make sure the endpoint is directly under the mouse.