Fabric.js Documentation

repository·gh-pages·Indexed 42 days ago

https://github.com/fabricjs/fabricjs.com

Documentation for Fabric.js, an interactive HTML5 canvas library that provides an object model on top of the native Canvas API. Includes guides on the object model hierarchy (fabric.Canvas, fabric.Object, and shapes), API references for image filters and drawing brushes, and upgrade guides for versions v1.x through v6.x.

Tokens
11.9K
Snippets
25
Records
37
Agent score
54%

What's inside Fabric.js

  1. Understand the Fabric.js object model hierarchy

    gh-pages

    Fabric.js is organized into a hierarchical object model. Understanding this hierarchy helps in locating specific APIs and understanding inheritance:

    Main rendering surfaces

    • fabric.Canvas: The interactive canvas.
    • fabric.StaticCanvas: A non-interactive canvas for static rendering.

    2D shapes (Inherit from fabric.Object)

    Most visual elements inherit from fabric.Object. Common shapes include:

    • Basic shapes: fabric.Rect, fabric.Circle, fabric.Triangle, fabric.Ellipse, fabric.Line, fabric.Polyline, fabric.Polygon.
    • Complex objects: fabric.Path, fabric.Group, fabric.Image, fabric.Text, and fabric.IText (interactive text).
    • Image Filters: fabric.Image supports various filters like Brightness, Grayscale, Sepia, Invert, etc.

    Visual styling components

    Objects can be filled or styled using:

    • fabric.Color, fabric.Gradient, fabric.Pattern, and fabric.Shadow.

    Utilities and Helpers

    • fabric.util: Contains helper modules for object, array, string, and ease operations.
    • fabric.Observable and fabric.Collection: Core namespace components for event handling and data management.

    Drawing Brushes

    Brushes used for free-drawing inherit from fabric.BaseBrush:

    • fabric.PencilBrush, fabric.CircleBrush, fabric.SprayBrush, and fabric.PatternBrush.
  2. Understand the Fabric.js object model vs. native Canvas API

    gh-pages

    Fabric.js provides an object model on top of the low-level HTML5 Canvas API.

    Native Canvas API works by executing immediate graphic commands on a 2D context (the bitmap). To change something, you often have to clear the entire canvas and redraw everything from scratch, or manually manage transformations like translate and rotate using radians.

    Fabric.js works by treating shapes as first-class objects. Instead of painting pixels, you instantiate objects (like fabric.Rect), set their properties (like left, top, fill, or angle), and add them to a fabric.Canvas instance. To update an object, you simply modify its properties and call canvas.renderAll() to refresh the view.

  3. Configure the `erasable` property on objects

    gh-pages

    The erasable property determines if a Fabric object can be affected by the EraserBrush. It accepts a boolean or the string 'deep'.

    • true (default): The object is erasable.
    • false: The object is protected from erasing.
    • 'deep': Used specifically for fabric.Group. It allows the eraser to target nested objects within the group individually if they are also marked as erasable. This provides fine-grained control but can impact performance in very large groups.

    Note on Groups: When erasable is set to true on a group, the entire group is erased. If you add or remove objects from a group, the eraser property is propagated to children. To prevent this propagation during object removal, set erasable to 'deep' before removal and restore it afterward.

    Note on Canvas Colors: Canvas#backgroundColor and Canvas#overlayColor are not fabric.Object instances and cannot be erased. To make them erasable, use a fabric.Rect at the bottom of the stack or use backgroundImage/overlayImage instead.

    // set in options
    const obj = new fabric.Rect({ erasable: false });
    
    // change existing object
    obj.set('erasable', true);
    
    // make all objects non erasable by default
    fabric.Object.prototype.erasable = false;
    
    // handling group `erasable` config
    const group = new fabric.Group([], { erasable: 'deep' });
    
    // setting a non-group object's `erasable` property to 'deep' is the same as setting it to true
    obj.set('erasable', 'deep');
  4. How custom controls work in FabricJS

    gh-pages

    In FabricJS 4.0+, customization is achieved through a controlSet rather than overriding standard methods.

    • The Control Set: fabric.Object.prototype contains a controlSet, which is a plain JavaScript object where keys represent control names (e.g., tl, mt, tr, mr, br, mb, bl, ml, mtr) and values are instances of the fabric.Control class.
    • Rendering Logic: The drawing engine iterates over the keys in the controlSet to render controls, meaning you can add, remove, or replace controls by modifying this object.
    • Scope: By default, all objects share the same control set. However, you can define unique control sets for specific subclasses (like fabric.Textbox) or dynamically show/hide controls based on the object's state.
    // Example of defining a custom control in a control set
    objectControls.mr = new fabric.Control({
      x: 0.5,
      y: 0,
      cursorStyleHandler: scaleSkewStyleHandler,
      actionHandler: scalingXOrSkewingY,
      getActionName: scaleOrSkewActionName,
    });
  5. Understand the Fabric.js object hierarchy and inheritance

    gh-pages

    Fabric.js objects are organized in a precise inheritance hierarchy. Most objects inherit from a root fabric.Object, which represents a 2-dimensional shape in a canvas plane. This base class provides common properties such as left, top, width, height, fill, stroke, angle, opacity, and more.

    Key Hierarchy Relationships

    • Root: fabric.Object (provides core graphic characteristics).
    • Direct Children of fabric.Object: fabric.Rect, fabric.Circle, fabric.Triangle, fabric.Ellipse, fabric.Polyline, fabric.Line, fabric.Path, fabric.Image, and fabric.Text.
    • Specialized Extensions:
      • fabric.Polygon extends fabric.Polyline.
      • fabric.Textbox extends fabric.IText, which in turn extends fabric.Text.

    Extending Functionality via Prototypes

    Because of this inheritance model, you can add methods to fabric.Object.prototype to make them available across all Fabric objects. This is a common pattern for advanced customization.

    Canvas Types

    • fabric.StaticCanvas: A plain, non-interactive canvas.
    • fabric.Canvas: A canvas with an interactive layer (supports object selection, manipulation, etc.).
    // Adding a custom method to all Fabric objects via the prototype
    fabric.Object.prototype.getAngleInRadians = function() {
      return this.get('angle') / 180 * Math.PI;
    };
    
    var rect = new fabric.Rect({ angle: 45 });
    console.log(rect.getAngleInRadians()); // 0.785...
    
    var circle = new fabric.Circle({ angle: 30, radius: 10 });
    console.log(circle.getAngleInRadians()); // 0.523...
    
    // Verification of inheritance
    console.log(circle instanceof fabric.Circle); // true
    console.log(circle instanceof fabric.Object); // true
  6. Maintain object position during polygon vertex manipulation

    gh-pages

    When modifying polygon points, the object's bounding box changes, causing the object to shift. To maintain a steady position, use an anchor point strategy:

    1. Identify an Anchor: Choose a point in the points array that is not currently being dragged.
    2. Capture Absolute Position: Before the actionHandler modifies the points, calculate the absolute canvas position of the anchor point using fabric.util.transformPoint and fabricObject.calcTransformMatrix().
    3. Recalculate Dimensions: Allow the actionHandler to update the points and trigger the object's internal dimension recalculation (e.g., via _setPositionDimensions).
    4. Restore Position: Calculate the anchor point's new relative position (a value between -0.5 and 0.5) and use fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5) to snap the object back so the anchor point stays in its original absolute location.
    function anchorWrapper(anchorIndex, fn) {
      return function(eventData, transform, x, y) {
        var fabricObject = transform.target,
            // Capture absolute position of anchor before modification
            absolutePoint = fabric.util.transformPoint({
                x: (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x),
                y: (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y),
            }, fabricObject.calcTransformMatrix()),
            
            actionPerformed = fn(eventData, transform, x, y),
            
            // Recalculate dimensions
            newDim = fabricObject._setPositionDimensions({}),
            polygonBaseSize = getObjectSizeWithStroke(fabricObject),
            
            // Calculate new relative origin
            newX = (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) / polygonBaseSize.x,
            newY = (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) / polygonBaseSize.y;
    
        // Snap object back to the absolute anchor position
        fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
        return actionPerformed;
      }
    }
  7. Use `before:path:created` and `path:created` events for path manipulation

    gh-pages

    When using drawing modes, you can intercept path creation using specific canvas events:

    • before:path:created: Triggered before a path is added to the canvas. This is useful for inspecting the path (e.g., calculating its length) and adding other objects (like text on that path) based on its properties before the path itself is finalized.
    • path:created: Triggered after a path has been added to the canvas. This can be used to remove the original path if you only intended to use it as a guide for other objects (like text).
    canvas.on('before:path:created', function(opt) {
      // Access the path via opt.path
      var path = opt.path;
      // Perform calculations or add text here
    });
    
    canvas.on('path:created', function(opt) {
      // Remove the path if it was only used as a template
      canvas.remove(opt.path);
    });
  8. Handle erasing events and lifecycle

    gh-pages

    The erasing process follows a specific lifecycle of events:

    1. erasing:start: Fired on mousedown.
      • Tip: To avoid rendering glitches when animating erasable objects, set their erasable property to false inside this event.
    2. Interaction: During mousemove, the main context is clipped by the brush and the top context is masked with relevant objects.
    3. path:created:before: Fired on mouseup. Use this to customize the path before it is applied to objects.
    4. erasing:end: Fired for every object that is mutated during the update process.
    5. erasing:end (Canvas level): Fired after all object updates are complete.
    6. path:created: Fired after the full erasing cycle concludes.

    Event Payload: The erasing:end event provides an object containing targets (the objects affected) and drawables.

  9. Implement erasing with EraserBrush

    gh-pages

    To enable erasing functionality, you must use a custom build of Fabric.js as erasing is not included in the default build.

    Once using a custom build, you can set the EraserBrush as the canvas's free drawing brush. You can also enable 'undo erasing' by setting the inverted property to true on the brush.

    // Set the eraser brush
    canvas.freeDrawingBrush = new fabric.EraserBrush(canvas);
    canvas.isDrawingMode = true;
    
    // Optional: adjust brush width
    canvas.freeDrawingBrush.width = 10;
    
    // Enable undo erasing
    canvas.freeDrawingBrush.inverted = true;
    // same as `PencilBrush`
    canvas.freeDrawingBrush = new fabric.EraserBrush(canvas);
    canvas.isDrawingMode = true;
      
    //  optional
    canvas.freeDrawingBrush.width = 10;
    
    //  undo erasing
    canvas.freeDrawingBrush.inverted = true;
  10. Upgrading from v5.x to v6.x (In Development)

    gh-pages

    Fabric.js v6.x is undergoing significant architectural changes. Key shifts include:

    • TypeScript Migration: The codebase is being migrated to TypeScript to improve development and debugging.
    • ES6 Adoption: Moving from ES5 to ES6 modules and syntax.
    • Promises over Callbacks: Asynchronous operations are moving from a callback-based pattern to a Promise-based pattern.
    • Group System Rewrite: The group system is being completely rewritten to address existing limitations.

    For a work-in-progress list of specific breaking changes, refer to the v6-breaking-changes issue tracker.