Boosting Performance in Fabric.js 5: Essential Optimization Techniques and Tips

Dinesh Rawat
calendar_month
April 16, 2024
timer
5 min
read time

Fabric.js is a versatile library that handles various interactions and rendering tasks on the Canvas element. It provides a solid foundation for creating interactive applications, but there are ways to further optimize its performance.

In this blog, we will explore practical examples of how you can improve the performance of Fabric.js and enhance user experience.

Tip 1: Utilize renderOnAddRemove

When initializing the canvas, you can improve performance by setting the renderOnAddRemove property to false. By doing this, the canvas won't rerender entirely whenever objects are added or removed.

Let's take a look at an example:


var canvas = new fabric.Canvas('canvas', { renderOnAddRemove: false });

Tip 2: Use enlivenObjects instead of loadFromJSON

Instead of using loadFromJSON to load serialized JSON data, you can manually parse the JSON and use enlivenObjects for better control over rendering and subsequent operations.

Here's an example:


var jsonString = '{"objects": [{ "type": "rect", "width": 200, "height": 200, "fill": "red" }]}';
var json = JSON.parse(jsonString);

fabric.util.enlivenObjects(json.objects, function(objects) {
    objects.forEach(function(obj) {
        canvas.add(obj);
    });
});

Tip 3: Optimize by excluding non-interactive shapes

To reduce the workload on Fabric.js, you can convert non-interactive layers into image representations. Render these layers as images and overlay the canvas on top. This can greatly improve performance, especially when dealing with complex shapes.

Here's an example:


// Create an image object
var img = new Image();
img.src = 'background.jpg';

// Wait for the image to load
img.onload = function() {
    // Create a fabric.Image from the loaded image
    var background = new fabric.Image(img);

    // Set the image as the background of the canvas
    canvas.setBackgroundImage(background, canvas.renderAll.bind(canvas));
};

Tip 4: Utilize fabric.StaticCanvas

If your application doesn't require interactivity, consider using fabric.StaticCanvas instead of fabric.Canvas. The StaticCanvas skips rendering controls, borders, and corner detection, resulting in improved performance.

Here's an example:


var canvas = new fabric.StaticCanvas('canvas');

Tip 5: Fine-tune object properties

By adjusting object properties, such as selection, controls, borders, rotating points, and canvas selection, you can optimize performance based on your specific needs.

Examples:


// Disable selection for an object
object.selectable = false;

// Disable controls and borders for an object
object.hasControls = false;
object.hasBorders = false;

// Disable rotating point for an object
object.hasRotatingPoint = false;

// Disable selection for the entire canvas
canvas.selection = false;

// Toggle skipTargetFind for mouse movement optimization
canvas.skipTargetFind = true; // or false, depending on your needs

Here's an example of disabling selection and controls for objects:


var rect = new fabric.Rect({
  width: 200,
  height: 200,
  fill: 'blue',
  selectable: false,
  hasControls: false
});

canvas.add(rect);

Tip 6: Enable object caching

Leveraging automatic object caching in Fabric.js can significantly improve performance, especially when dealing with complex objects or large SVGs. You can customize caching behavior using properties like objectCaching, statefulCache, noScaleCache, dirty, and needsItsOwnCache.

Here's an example:


var circle = new fabric.Circle({
  radius: 50,
  fill: 'green',
  objectCaching: true
});

canvas.add(circle);

Tip 7: Use requestAnimationFrame

To ensure smooth and efficient animation, utilize the requestAnimationFrame method to update the canvas, synchronizing it with the browser's rendering engine.

Here's an example:


function animate() {
  // Perform animation logic here

  // Request the next animation frame
  fabric.util.requestAnimFrame(animate);
}

// Start the animation loop
animate();

Tip 8: Minimize redraws

Avoid unnecessary redraws by tracking changes with the dirty property and selectively rendering only the objects that require updates.

Here's an example:


// Set the dirty property of an object
object.dirty = true;

// Render only the dirty objects
canvas.renderOnAddRemove = false;
canvas.renderAll();
canvas.renderOnAddRemove = true;

Tip 9: Use object grouping

By grouping objects using fabric.Group, you can reduce the number of rendered and updated objects, while still retaining the ability to interact with them as a single entity.

Here's an example:


var rect1 = new fabric.Rect({ width: 100, height: 100, fill: 'red' });
var rect2 = new fabric.Rect({ width: 100, height: 100, fill: 'blue' });

var group = new fabric.Group([rect1, rect2]);

canvas.add(group);

Tip 10: Limit object complexity

Simplifying or optimizing complex objects with numerous points, paths, or patterns can greatly enhance rendering and manipulation speed. Consider simplifying the geometry or reducing the number of points in paths to improve performance.

Tip 11: Use event delegation for interactivity

Improve performance by attaching a single event listener to the canvas and utilizing event delegation to handle events for multiple objects efficiently.

Here's an example:


canvas.on('mouse:down', function (event) {
  var target = event.target;
  // Handle event for the target object
});

Tip 12: Optimize rendering performance with fabric.util.requestRenderAll()

When making multiple canvas changes, you can optimize rendering performance by using fabric.util.requestRenderAll() to batch rendering calls. This prevents unnecessary rendering after each individual change.

Here's an example:


fabric.util.requestRenderAll(function () {
  // Perform multiple canvas changes here
});

Tip 13: Optimize image loading with lazy loading

Implement lazy loading techniques to load images only when necessary. This reduces the initial load time and improves overall performance.

Here's an example:


var img = new Image();
img.onload = function () {
  var fabricImage = new fabric.Image(img);
  canvas.add(fabricImage);
};
img.src = 'image.jpg';

Tip 14: Dispose of unused objects and canvases

Properly removing objects from the canvas and disposing of unused canvases can optimize memory usage and improve performance. Make sure to call the remove method to remove objects and use the dispose method to free up resources for unused canvases.

Here's an example:

By applying these optimization techniques, you can maximize the performance of Fabric.js and provide a superior user experience in your applications. Remember to tailor these techniques to your specific needs and always measure the impact of optimizations to ensure they are effective.

In conclusion, here are the key points to remember for optimizing the performance of Fabric.js:

  1. Set renderOnAddRemove to false to improve performance when adding or removing objects.
  2. Use enlivenObjects instead of loadFromJSON for better control over rendering and operations.
  3. Convert non-interactive layers into images to reduce the workload on Fabric.js.
  4. Consider using fabric.StaticCanvas for non-interactive applications.
  5. Fine-tune object properties such as selection and controls to optimize performance.
  6. Enable object caching for complex objects or large SVGs.
  7. Utilize requestAnimationFrame for smooth and efficient animation.
  8. Minimize redraws by tracking changes and rendering only the necessary objects.
  9. Group objects with fabric.Group to reduce the number of rendered and updated objects.
  10. Simplify or optimize complex objects to enhance rendering and manipulation speed.
  11. Use event delegation for efficient handling of events on multiple objects.
  12. Optimize rendering performance with fabric.util.requestRenderAll() when making multiple canvas changes.
  13. Implement lazy loading techniques to load images only when necessary.
  14. Dispose of unused objects and canvases to optimize memory usage.

By following these tips and techniques, you can significantly enhance the performance of your Fabric.js applications, providing users with a smooth and responsive experience. Experiment with these optimizations and measure their impact to ensure the best results for your specific use cases.