Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Mastering the Fabric.js SprayBrush: From Basic Configuration to Advanced Customization

Tech 3

Introduction

The SprayBrush tool in Fabric.js is a versatile and engaging feature, offering a wide range of configurable properties that make it straightforward to use.

Basic Configuraton

To use the spray brush, the canvas must first be set too "drawing mode."

Set isDrawingMode to true to enable this mode.

<canvas id="canvasElement" width="500" height="400" style="border: 1px solid #ccc;"></canvas>

<script>
  // Initialize canvas with drawing mode enabled
  const drawingCanvas = new fabric.Canvas('canvasElement', {
    isDrawingMode: true
  });
</script>

Drawing mode can also be enabled after initialization:

drawingCanvas.isDrawingMode = true;

To return to normal mode, simply set isDrawingMode back too false.

Registering the Spray Brush

The spray brush is implemented as SprayBrush.

When registernig, pass the initialized canvas to the brush constructor and assign it to canvas.freeDrawingBrush.

// Assign spray brush directly
drawingCanvas.freeDrawingBrush = new fabric.SprayBrush(drawingCanvas);

// Recommended approach: store brush in a variable for easier configuration
let sprayTool = new fabric.SprayBrush(drawingCanvas);
drawingCanvas.freeDrawingBrush = sprayTool;

Alternatively, you can use the initialize() method:

let sprayTool = new fabric.SprayBrush();
sprayTool.initialize(drawingCanvas);
drawingCanvas.freeDrawingBrush = sprayTool;

The initialize() method accepts the canvas instance as its parameter.

Setting Brush Width

To demonstrate other properties more clearly, let's first increase the brush width.

sprayTool.width = 200;

The width property controls the brush thickness—higher values create thicker sprays.

Configuring Spray Density

The density property determines the concentration of spray points. Higher values increase density.

Default density is 20.

sprayTool.width = 200;
sprayTool.density = 100; // Increase spray density

Compare with lower density:

sprayTool.width = 200;
sprayTool.density = 10;

The difference is visually apparent.

Adjusting Dot Size

Each individual point in the spray is called a "dot." The dotWidth property controls dot size.

Default dotWidth is 1. Larger values create bigger dots.

sprayTool.width = 200;
sprayTool.dotWidth = 10; // Increase dot size

Setting Dot Size Variance

The dotWidthVariance property introduces randomness in dot sizes within a specified range.

Default dotWidthVariance is 1. Higher values create greater size variation.

sprayTool.width = 200;
sprayTool.dotWidthVariance = 10;

When dotWidthVariance is set, dotWidth becomes less significant.

Preventing Overlap Removal

By default, the spray brush removes overlapping dots for performance reasons.

To disable this behavier, set optimizeOverlapping to false.

sprayTool.optimizeOverlapping = false;

Enabling Random Opacity

The randomOpacity property randomizes the opacity of spray dots.

sprayTool.randomOpacity = true;

Adding Shadow Effects

Although not explicitly documented for the spray brush, shadow effects can be applied similarly to basic brushes.

sprayTool.width = 200;
sprayTool.dotWidthVariance = 10;

// Apply shadow effect
sprayTool.shadow = new fabric.Shadow({
  blur: 10,
  offsetX: 10,
  offsetY: 10,
  color: '#30e3ca'
});

Setting Spray Color

The color property controls the spray color, though it's not well-documented.

sprayTool.color = 'pink';

Event Handling

Beyond the initialize() method, the spray brush provides several event handlers.

Before and After Path Creation

Since spray creates a path, you can listen to canvas path creation events.

  • before:path:created: Triggered before spray generation
  • path:created: Triggered after spray generation
// Before generation
drawingCanvas.on('before:path:created', event => {
  console.log(event.path);
});

// After generation
drawingCanvas.on('path:created', event => {
  console.log(event.path);
});

Mouse Down Event

sprayTool.onMouseDown = function(mousePosition) {
  this.sprayChunks.length = 0;
  this.canvas.clearContext(this.canvas.contextTop);
  this._setShadow();
  this.addSprayChunk(mousePosition);
  this.render(this.sprayChunkPoints);
};

Mouse Move Event

sprayTool.onMouseMove = function(mousePosition) {
  if (!this.limitedToCanvasSize || !this._isOutSideCanvas(mousePosition)) {
    this.addSprayChunk(mousePosition);
    this.render(this.sprayChunkPoints);
  }
};

Mouse Up Event

sprayTool.onMouseUp = function() {
  var originalRenderSetting = this.canvas.renderOnAddRemove;
  this.canvas.renderOnAddRemove = false;
  
  var sprayElements = [];
  
  for (var chunkIndex = 0; chunkIndex < this.sprayChunks.length; chunkIndex++) {
    for (var pointIndex = 0; pointIndex < this.sprayChunks[chunkIndex].length; pointIndex++) {
      var currentPoint = this.sprayChunks[chunkIndex][pointIndex];
      
      var sprayDot = new fabric.Rect({
        width: currentPoint.width,
        height: currentPoint.width,
        left: currentPoint.x + 1,
        top: currentPoint.y + 1,
        originX: "center",
        originY: "center",
        fill: this.color
      });
      
      sprayElements.push(sprayDot);
    }
  }
  
  if (this.optimizeOverlapping) {
    sprayElements = this._getOptimizedRects(sprayElements);
  }
  
  var sprayGroup = new fabric.Group(sprayElements);
  
  if (this.shadow) {
    sprayGroup.set("shadow", new fabric.Shadow(this.shadow));
  }
  
  this.canvas.fire("before:path:created", { path: sprayGroup });
  this.canvas.add(sprayGroup);
  this.canvas.fire("path:created", { path: sprayGroup });
  
  this.canvas.clearContext(this.canvas.contextTop);
  this._resetShadow();
  this.canvas.renderOnAddRemove = originalRenderSetting;
  this.canvas.requestRenderAll();
};

Advanced Customization

Customizing Spray Dot Shapes

The key to customizing spray behavior lies in the onMouseUp event handler.

The default implementation uses Rect elements for spray dots. What if we use different shapes?

Let's try circles instead:

sprayTool.onMouseUp = function() {
  var originalRenderSetting = this.canvas.renderOnAddRemove;
  this.canvas.renderOnAddRemove = false;
  
  var sprayElements = [];
  
  for (var chunkIndex = 0; chunkIndex < this.sprayChunks.length; chunkIndex++) {
    for (var pointIndex = 0; pointIndex < this.sprayChunks[chunkIndex].length; pointIndex++) {
      var currentPoint = this.sprayChunks[chunkIndex][pointIndex];
      
      // Use Circle instead of Rect
      const sprayCircle = new fabric.Circle({
        radius: currentPoint.width,
        top: currentPoint.y + 1,
        left: currentPoint.x + 1,
        originX: "center",
        originY: "center",
        fill: this.color
      });
      
      sprayElements.push(sprayCircle);
    }
  }
  
  if (this.optimizeOverlapping) {
    sprayElements = this._getOptimizedRects(sprayElements);
  }
  
  var sprayGroup = new fabric.Group(sprayElements);
  
  if (this.shadow) {
    sprayGroup.set("shadow", new fabric.Shadow(this.shadow));
  }
  
  this.canvas.fire("before:path:created", { path: sprayGroup });
  this.canvas.add(sprayGroup);
  this.canvas.fire("path:created", { path: sprayGroup });
  
  this.canvas.clearContext(this.canvas.contextTop);
  this._resetShadow();
  this.canvas.renderOnAddRemove = originalRenderSetting;
  this.canvas.requestRenderAll();
};

This circular spray resembles the effect of the CircleBrush.

You can experiment with other Fabric.js shapes or even custom shapes by modifying the element creation in the onMouseUp handler.

Creating Random Color Spray

Building on the shape customization concept, creating a random color spray is straightforward—simply assign different colors to each dot.

sprayTool.onMouseUp = function() {
  var originalRenderSetting = this.canvas.renderOnAddRemove;
  this.canvas.renderOnAddRemove = false;
  
  var sprayElements = [];
  
  for (var chunkIndex = 0; chunkIndex < this.sprayChunks.length; chunkIndex++) {
    for (var pointIndex = 0; pointIndex < this.sprayChunks[chunkIndex].length; pointIndex++) {
      var currentPoint = this.sprayChunks[chunkIndex][pointIndex];
      
      // Generate random RGB values for each dot
      let red = Math.floor(Math.random() * 255);
      let green = Math.floor(Math.random() * 255);
      let blue = Math.floor(Math.random() * 255);
      
      var sprayDot = new fabric.Rect({
        width: currentPoint.width,
        height: currentPoint.width,
        left: currentPoint.x + 1,
        top: currentPoint.y + 1,
        originX: "center",
        originY: "center",
        fill: `rgb(${red}, ${green}, ${blue})` // Unique color for each dot
      });
      
      sprayElements.push(sprayDot);
    }
  }
  
  if (this.optimizeOverlapping) {
    sprayElements = this._getOptimizedRects(sprayElements);
  }
  
  var sprayGroup = new fabric.Group(sprayElements);
  
  if (this.shadow) {
    sprayGroup.set("shadow", new fabric.Shadow(this.shadow));
  }
  
  this.canvas.fire("before:path:created", { path: sprayGroup });
  this.canvas.add(sprayGroup);
  this.canvas.fire("path:created", { path: sprayGroup });
  
  this.canvas.clearContext(this.canvas.contextTop);
  this._resetShadow();
  this.canvas.renderOnAddRemove = originalRenderSetting;
  this.canvas.requestRenderAll();
};

This example demonstrates the concept, though the random color combination may not be aesthetically pleasing.

Tags: Fabric.js

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.