/ best free open source web resources /

Fabric.js: A Powerful & Simple JavaScript Canvas Library

In this article, I’ll introduce you to Fabric.js—a powerful JavaScript library that makes working with the HTML5 canvas element a breeze. Fabric provides a missing object model for canvas, as well as an SVG parser, a layer of interactivity, and a whole suite of other indispensable tools. It is a fully open-source project, licensed under MIT, with many contributions over the years.

I started developing with Fabric three years ago after discovering the pains of working with the native canvas API. I was creating an interactive design editor for printio.ru—my startup that allows users to design their own apparel. The kind of interactivity I wanted existed only in Flash apps in those days. Now, very few libraries come close to what is possible with Fabric, so let’s take a closer look.

Why Fabric?

Canvas allows you to create some absolutely amazing graphics on the Web these days, but the API it provides is disappointingly low level. It’s one thing if you simply want to draw a few basic shapes on a canvas and forget about them. If you need any kind of interaction, to change a picture at any point, or to draw more complex shapes, the situation changes dramatically. Fabric aims to solve this problem.

Native canvas methods allow you only to fire off simple graphic commands, blindly modifying the entire canvas bitmap. Do you want to draw a rectangle? Use fillRect(left, top, width, height). Want to draw a line? Use a combination of moveTo(left, top) and lineTo(x, y). It’s as if you’re painting a canvas with a brush, layering more and more oil or acrylic on top, with very little control.

Instead of operating at such a low level, Fabric provides a simple but powerful object model on top of native methods. It takes care of canvas state and rendering and lets you work with objects directly.

Here’s a simple example that demonstrates this difference. Let’s say you want to draw a red rectangle somewhere on the canvas. Here’s how you would do it with the native canvas API:

At this point, there’s almost no difference in the size of the rectangle—the two examples are pretty similar. However, you can already see how different the approach to working with canvas is. With native methods, you operate on context—an object representing the entire canvas bitmap. In Fabric, you operate on objects—you instantiate them, change their properties, and add them to the canvas. You can see that these objects are first-class citizens in Fabric land.

Rendering a plain red rectangle is too simple. You can at least have some fun with it and perhaps the shape slightly. Let’s try 45 degrees, first using native canvas methods:

What’s happening here? All you have to do in Fabric is change the object’s angle value to 45. With native methods, however, more work is required. Remember that you can’t operate on objects. Instead, you have to tweak the positioning and angle of the entire canvas bitmap (ctx.translate, ctx.rotate) to suit your needs. You then draw the rectangle again, remembering to offset the bitmap properly (-10, -10) so that it’s still rendered at the point of 100,100. As a bonus, you have to translate degrees to radians when rotating the canvas bitmap.

I’m sure you’re starting to see why Fabric exists and how much low-level boilerplate it hides.

Let’s take a look at another example: keeping track of canvas state.

What if at some point, you want to move the rectangle to a slightly different location on the canvas? How can you do this without being able to operate on objects? Would you just call another fillRect on a canvas bitmap? Not quite. Calling another fillRect command actually draws a rectangle on top of whatever is already drawn on the canvas. To move the rectangle, you need to first erase any previously drawn content and then draw the rectangle at a new location (see Figure 3).

Notice a very important difference: with Fabric, you don’t need to erase the content before attempting to modify any content. You still work with objects simply by changing their properties and then render the canvas again to get a fresh picture.

Objects

You saw in the last section how to work with rectangles by instantiating the fabric.Rect constructor. Fabric, of course, covers the other basic shapes as well—circles, triangles, ellipses, and so on. The shapes are exposed under the fabric “namespace” as fabric.Circle, fabric.Triangle, fabric.Ellipse, and so on. Fabric provides seven basic shapes:

You do the same thing with any other basic shape. Figure 4 shows an example of a green circle drawn at location 100,100 and a blue triangle at 50,50.

Figure 4 A Blue Triangle and a Green Circle Drawn with Fabric

Manipulating objects

Creating graphical objects—rectangles, circles, or something else—is only the beginning. At some point, you will probably need to modify your objects. Perhaps a certain action will trigger a change of state or play an animation of some sort. Or you might want to change object properties (such as color, opacity, size, position) on certain mouse interactions.

Fabric takes care of canvas rendering and state management for you. We need only to modify the objects themselves. The example earlier demonstrated the set method and how calling set({ left: 20, top: 50 }) moved the object from its previous location. In a similar fashion, you can change any other property of an object.

First, the fill value is set to “red”. The next statement sets the strokeWidth and stroke values, giving the rectangle a 5 px stroke of a pale green color. Finally, the code changes the angle and flipY properties. Notice how each of the three statements uses slightly different syntax.

This demonstrates that set is a universal method. You will probably use it quite often, and it’s meant to be as convenient as possible. What about getters? There’s a generic get method and also a number of specific ones. To read the width property of an object, you use get(‘width’) or getWidth(). To get the scaleX value, you would use get(‘scaleX’), getScaleX() and so on. There’s a method like getWidth or getScaleX for each of the “public” object properties (stroke, strokeWidth, angle, and so on).

You might have noticed that in the earlier examples, objects were created with the same configuration hash as the one we just used in the set method. You can “configure” an object at the time of creation or use the set method later:

This rectangle has a default set of properties. It’s positioned at 0,0, is black and fully opaque, and has no stroke and no dimensions (width and height are 0). Because no dimensions are given, you can’t see it on the canvas. Giving it any positive values for width and height would reveal a black rectangle at the top-left corner of the canvas, as shown in Figure 6.

Figure 6 How Default Rectangle Looks When Given Dimensions

Hierarchy and Inheritance

Fabric objects do not exist independently of each other. They form a very precise hierarchy. Most objects inherit from the root fabric.Object. The fabric.Object root object represents (more or less) a two-dimensional shape, positioned in a two-dimensional canvas plane. It’s an entity that has left/top and width/height properties, as well as a slew of other graphical characteristics. The properties listed for objects—fill, stroke, angle, opacity, flip*, and so on—are common to all Fabric objects that inherit from fabric.Object.

This inheritance allows you to define methods on fabric.Object and share them among all child “classes”. For example, if you want to have a getAngleInRadians method on all objects, you would simply create it on fabric.Object.prototype, as follows:

As you can see, the method immediately becomes available on all instances.

Even though child “classes” inherit from fabric.Object, they often also define their own methods and properties. For example, fabric.Circle needs a radius property, and fabric.Image—which we’ll look at in a moment—needs getElement and setElement methods for accessing and setting the HTML <img> element from which an image instance originates.

Canvas

Now that you’ve learned about objects in some detail, let’s get back to canvas.

The first thing you see in all of the Fabric examples is the creation of a canvas object— new fabric.Canvas(‘…’). The fabric.Canvas object serves as a wrapper around the <canvas> element and is responsible for managing all the Fabric objects on that particular canvas. It takes an ID of an element and returns an instance of fabric.Canvas.

You can add objects to it, reference them from it, or remove them, as shown here:

Managing objects is the main purpose of fabric.Canvas, but it also serves as a configuration host. Do you need to set the background color or image for an entire canvas, clip all contents to a certain area, set a different width and height, or specify whether a canvas is interactive or not? All these options (and others) can be set on fabric.Canvas, either at the time of creation or later. Figure 7 shows an example.

Interactivity

One of the unique built-in features of Fabric is a layer of interactivity on top of the object model. The object model exists to allow programmatic access and manipulation of objects on the canvas, but on the outside—on a user level—there’s a way to manipulate those objects via the mouse (or via touch on touch devices). As soon as you initialize a canvas via the new fabric.Canvas(‘…’) call, it’s possible to select objects (see Figure 8), drag them around, scale or rotate them, and even group them (see Figure 9) to manipulate them in one chunk!

Figure 8 Red, Rotated Rectangle in Selected State (Controls Visible)

Figure 9 Rectangle and Circle Grouped (Controls Visible)

If you want to allow users to drag something on the canvas—let’s say an image—all you need to do is initialize the canvas and add an object to it. No additional configuration or setup is required.

To control this interactivity, you can use Fabric’s selection Boolean property on the canvas object in combination with the selectable Boolean property of individual objects:

This creates a “lighter” version of canvas, without any event-handling logic. You still have the entire object model to work with—adding, removing or modifying objects, as well as changing any canvas configuration. All of this still works, it’s only event handling that’s gone.

Later in this article, when I go over the custom build option, you’ll see that if StaticCanvas is all you need, you can even create a lighter version of Fabric. This could be a nice option if you need something like non-interactive charts or non-interactive images with filters in your application.

Images

Adding rectangles and circles to a canvas is fun, but as you can imagine by now, Fabric also makes working with images very easy. Here’s how you instantiate the fabric.Image object and add it to a canvas, first in HTML and then in JavaScript:

Notice that you pass an image element to the fabric.Image constructor. This creates an instance of fabric.Image that looks just like the image from the document. Moreover, you immediately set left/top values to 100/100, angle to 30, and opacity to 0.85. Once an image is added to a canvas, it is rendered at location 100,100 at a 30-degree angle and is slightly transparent (see Figure 10). Not bad!

Looks pretty straightforward, doesn’t it? Just call fabric.Image.fromURL, with a URL of an image, and give it a callback to invoke once the image is loaded and created. The callback function receives the already created fabric.Image object as its first argument. At that point, you can add it to your canvas or perhaps change it first and then add it, as shown here:

Path and PathGroup

We’ve looked at simple shapes and images. What about more complex, richer shapes and content? Meet Path and PathGroup, the power couple.

Paths in Fabric represent an outline of a shape, which can be filled, stroked and modified in other ways. Paths consist of a series of commands that essentially mimic a pen going from one point to another. With the help of such commands as move, line, curve, and arc, Paths can form incredibly complex shapes. And with the help of groups of Paths (PathGroup), the possibilities open up even more. Paths in Fabric closely resemble SVG
elements. They use the same set of commands, can be created from
elements, and can be serialized into them. I’ll describe more about serialization and SVG parsing later, but for now it’s worth mentioning that you will probably only rarely create Path instances by hand. Instead, you’ll use Fabric’s built-in SVG parser. But to understand what Path objects are, let’s create a simple one by hand (see Figure 11 for the results):

Here you instantiate the fabric.Path object and pass it a string of path instructions. It might look cryptic, but it’s actually easy to understand. M represents the move command and tells the invisible pen to move to point 0, 0. L stands for line and makes the pen draw a line to point 200, 100. Then, another L creates a line to 170, 200. Lastly, z forces the drawing pen to close the current path and finalize the shape.

Since fabric.Path is just like any other object in Fabric, you can also change some of its properties, or modify it even more, as shown here and in Figure 12:

Here, M still stands for the move command, so the pen starts its drawing journey at point 121.32, 0. Then there’s an L (line) command that brings the pen to 44.58, 0. So far so good. Now comes the C command, which stands for “cubic bezier.” This command makes the pen draw a bezier curve from the current point to 36.67, 0. It uses 29.5, 3.22 as a control point at the beginning of a line, and 24.31, 8.41 as the control point at the end of the line. This whole operation is then followed by a dozen other cubic bezier commands, which finally create a nice-looking shape of an arrow, as shown in Figure 13.

Figure 13 Complex Path Rendered with Fabric

Chances are, you won’t work with such beasts directly. Instead, you can use something like the fabric.loadSVGFromString or fabric.loadSVGFromURL method to load an entire SVG file and let Fabric’s SVG parser do its job of walking over all SVG elements and creating corresponding Path objects.

In this context, while Fabric’s Path object usually represents a SVG
element, a collection of paths, often present in SVG documents, is represented as a PathGroup instance (fabric.PathGroup). PathGroup is nothing but a group of Path objects, and because fabric.PathGroup inherits from fabric.Object, it can be added to a canvas just like any other object and manipulated the same way.

Just like with Paths, you probably won’t be working with a PathGroup directly. But if you stumble on one after parsing a SVG document, you’ll know exactly what it is and what purpose it serves.

Wrapping Up For Now

I’ve only scratched the surface of what’s possible with Fabric. You can now easily create any of the simple shapes, complex shapes, or images; add them to a canvas and modify them any way you want—their positions, dimensions, angles, colors, strokes, opacity—you name it.

About the Author

Juriy Zaytsev is a passionate JavaScript developer living in New York. He is an ex-Prototype.js core member, blogger at perfectionkills.com, and the creator of Fabric.js canvas library. Currently Juriy works on his Printio.ru startup and making Fabric even more fun to use.