The Invent with Python Blog

Writings from the author of Automate the Boring Stuff.

Fabric.js Tutorial Part 1

Sat 04 May 2024    Al Sweigart

What is Fabric.js?

Fabric.js is a JavaScript library that makes drawing on HTML5 <canvas> elements easier than using the native Canvas API. This Fabric.js tutorial emulates the format of Hunor Márton Borbély's SVG Tutorial at svg-tutorial.com: short demonstrations that you can enter into a text editor and view in your local browser.

I dislike code snippets that you can't actually run yourself on your own computer with modification. I will always give you the complete HTML for each example that you can copy and open in your browser on your computer. There are no missing sections or "leave it to the reader" omissions. Furthermore, the Fabric.js examples on each page are live examples and not just screenshots. I also provide JSFiddle links with the example code for you to modify.

The Fabric.js website has tutorials, documentation, and a gallery of examples but you won't need to consult that site to follow this tutorial series. This tutorial does assume you have basic HTML and JavaScript knowledge.

Drawing Rectangles and Circles

Let's create a small example with red and blue rectangles on a <canvas> element that will look like this:

In Fabric.js, you draw basic shapes by creating shape objects with JavaScript code. You can also write text, load images, and apply image filters, but we'll explore that later. You can see the complete code for this example on its own page at fabric-js-rects-and-circle.html. Right-click the page and select "View Source" to view the HTML and JavaScript code. You can also view this code on JSFiddle.

Notice that you can drag the objects around the canvas as well as scale and rotate them. This feature is automatically provided by Fabric.js. To disable this, replace the fabric.Canvas() code (we'll see this in the next section) with fabric.StaticCanvas(), as in this output where you can't move the shapes around:

Notice that for objects added to the static canvas, you cannot select them and move them around.

Let's look at each part of the HTML and JavaScript individually:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Fabric.js Rects and Circle</title>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.js"></script>
</head>

This is some standard HTML boilerplate for a basic web page header. I'm loading the Fabric.js library from a free CDN (Content Distribution Network) provided by Cloudflare, but you can put the fabric.js file on your own web host and link to it instead. This tutorial uses Fabric.js version 5.3.1. The CDN also provides a minified version for faster loading if you change the filename to fabric.min.js in this URL.


<body>
  <!-- The HTML <canvas> element: -->
  <canvas id="mainCanvasId" width="400" height="400" style="border: 1px solid black;"></canvas>
</body>

The <canvas> element is the area where the drawing takes place. You can think of it as a <img> element but its contents come from JavaScript code instead of an image file. Instead of using the native Canvas API to do the drawing, you can use Fabric.js's API. I've given this <canvas> element a width and height of 400 pixels, as well as a thin black border for easier viewing.


<script>
// Create a Fabric.js "Canvas" object that is tied to the HTML <canvas> element:
let canvasObj = new fabric.Canvas('mainCanvasId');

First, create a new Canvas object by passing fabric.Canvas() the id of the <canvas> element, 'mainCanvasId'. This is necessary since there could be multiple <canvas> elements on the web page.


// Create a Fabric.js "Rect" object that represents a rectangle:
let rectObj = new fabric.Rect({
  left: 100, top: 50, fill: 'red', stroke: 'blue', width: 80, height: 30
});

// The rectangle won't appear on the canvas until it is added:
canvasObj.add(rectObj);

We'll draw a rectangle by creating a Rect object. Pass a JS object with keys 'left' and 'top' for the X and Y coordinate of the left and top edge of the rectangle. The interior fill color for this rectangle will be red and the outline stroke will be blue. The size of the rectangle is 80 pixels wide and 30 pixels tall. (Remember that we made the total size of the <canvas> element 400 x 400.)

You won't see this rectangle on the canvas until you call the Canvas.add() method with the Rect objects.


// Create a second rectangle:
let rectObj2 = new fabric.Rect({
  left: 50, top: 100, fill: 'green', width: 50, height: 50
});

canvasObj.add(rectObj2);

We'll create a second rectangle, this time with a green fill and size of 50 pixels by 50 pixels.


// Create a circle:
let circleObj = new fabric.Circle({
  // Note that the radius is half the width of the circle.
  radius: 100, fill: '#FF00FF', left: 200, top: 200
});

canvasObj.add(circleObj);
</script>

The Circle object must be added to the Canvas object the same way the Rect objects are, or you will not see them on the canvas. Note that objects added to the canvas most recently are on top of any objects added earlier.

Next, let's create a magenta circle with a radius of 100 pixels by calling the fabric.Circle() function. The fill property in the object passed to this function is set to '#FF00FF', an HTML RGB code for maximum red, no green, and maximum blue to produce magenta. Notice that the position of the circle is set by its left and top edge location. If you want to position the circle by supplying its center X and Y coordinates, include originX: 'center', originY: 'center' in the object passed to fabric.Circle(). Somewhat confusingly, the left and top parameter names will now set the center position of the shape instead of the left and top edge.

For example, changing the code to let circleObj = new fabric.Circle({radius: 100, fill: '#FF00FF', left: 200, top: 200, originX: 'center', originY: 'center'}); will make the canvas look like this:

Note that forgetting to set either the width or height in fabric.Rect() or the radius in fabric.Circle() will cause that shape to not render on the canvas. There will be no error in the browser's developer console to indicate that an error has occured; the shape will simply not appear.

Review:

  • Put <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.js"></script> in your HTML file to load the Fabric.js library from a free CDN.
  • Create the <canvas> element with <canvas id="mainCanvasId" width="400" height="400" style="border: 1px solid black;"></canvas>
  • The code let canvasObj = new fabric.Canvas('mainCanvasId'); creates a Canvas object.
  • The code let rectObj = new fabric.Rect({ left: 100, top: 50, fill: 'red', stroke: 'blue', width: 80, height: 30 }); creates a rectangle.
  • The code let circleObj = new fabric.Circle({radius: 100, fill: '#FF00FF', left: 200, top: 200}); creates a circle. Instead of width and height, it has a radius.
  • You must call canvasObj.add(rectObj); to make a shape actually appear on the canvas; creating it is not enough.
  • When creating a new shape, you can set originX to left, center, or right and set originY to top, center, bottom to change which point on the shape the left and top properties set. (However, these properties will still confusingly be named left and top.)
  • If a shape doesn't appear on the canvas, check that you remembered to supply a width and height property (for rectangles) or a radius property (for circles). The left and top properties may also have the position of the shape outside the view of the canvas.

Project: Draw a Pine Tree

Let's draw a pine tree with the fabric.Triangle shape. We'll create this tree out of three triangles and one rectangle:

The full code is at fabric-js-pine-tree.html or on JSFiddle. Let's examine each part individually:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Fabric.js Pine Tree</title>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
</head>

<body>
  <!-- The HTML <canvas> element: -->
  <canvas id="treeCanvasId" width="200" height="200" style="border: 1px solid black;"></canvas>
</body>

This is the standard boilerplate HTML for a web page. Note that the ID we use for the <canvas> element is treeCanvasId and it is a 200 x 200 pixels in size.


<script>
// Create a Fabric.js "Canvas" object that is tied to the HTML <canvas> element:
let canvasObj = new fabric.Canvas('treeCanvasId');

let branches1 = new fabric.Triangle({
  width: 80, height: 80, fill: '#234236', left: 100, top: 20
});

let branches2 = new fabric.Triangle({
  width: 100, height: 60, fill: '#0C5C4C', left: 90, top: 70
});

let branches3 = new fabric.Triangle({
  width: 110, height: 50, fill: '#38755B', left: 85, top: 110
});

First, we need a fabric.Canvas object to be the Fabric.js representation of the HTML <canvas> element. Then branches of our pine tree will be green triangles that we create by creating fabric.Triangle objects. These isosceles triangles point upwards, but we'll learn how to rotate them in the next tutorial part.

Note that the size and position are given the same way as fabric.Rect rectangles: width, height, left, and top. We don't specify a triangle's shape and size by specifying three pairs of X and Y coordinates. (For this kind of triangle, we'd use the fabric.Polygon shape that we'll cover in later tutorial parts.) We color them with different shades of green: #234236, #0C5C4C, and #38755B.


let stump = new fabric.Rect({
  width: 20, height: 30, fill: 'brown', left: 130, top: 150
});

The brown stump will be a rectangle created with a fabric.Rect object.


canvasObj.add(branches3);
canvasObj.add(branches2);
canvasObj.add(branches1);
canvasObj.add(stump);
</script>

We've created these four shape objects, but they won't appear on the canvas until after we call the add() method. I want the top branches (in branches1) to appear on top of the other triangles, so I need to add it last.

Review:

  • The fabric.Triangle shape draws isoceles triangles pointing upwards with let branches1 = new fabric.Triangle({width: 80, height: 80, fill: '#234236', left: 100, top: 20});
  • The order that the shapes are created doesn't matter, rather the first shapes added with add() will be on the bottom of the canvas underneath shapes added later.

This tutorial continues on to Part 2. Special thanks to Hunor Márton Borbély for the SVG tutorial that inspired this Fabric.js tutorial.


Learn to program for free with my books for beginners:

Sign up for my "Automate the Boring Stuff with Python" online course with this discount link.

Email | Mastodon | Twitter | Twitch | YouTube | GitHub | Blog | Patreon | LinkedIn | Personal Site