Search code examples
javascripthtmlcssfabricjs

Show outside element part with opacity - fabric js - controlsAboveOverlay


The controlsAboveOverlay is a property in Fabric.js. It's a boolean that, when set to true, renders controls (borders, corners, etc.) of an object above the overlay image. The overlay image is an image that can be set to display on top of the canvas.

Currently, it is giving the below result. The outside part has only controls.

enter image description here

I would like to show the outside part of the element with an opacity of 0.5. Like below.

enter image description here

Here, is a sample HTML code.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Fabric.js Example with controlsAboveOverlay</title>
    <!-- Importing the Fabric.js library -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>
  </head>
  <body>
    <!-- Creating a canvas element with id 'canvas' -->
    <canvas id="canvas" width="400" height="400"></canvas>
    <style>
      .canvas-container {
        /* Setting the background color of the canvas container */
        background-color: #f0f0f0;
      }
    </style>
    <script>
      (function () {
        /* Initializing a new Fabric.js canvas with certain properties */
        var canvas = new fabric.Canvas("canvas", {
          width: 600,
          height: 500,
          backgroundColor: "#ffffff",
          /* Setting controlsAboveOverlay to true to render controls above the overlay image */
          controlsAboveOverlay: true,
        });

        /* Creating a rectangular clip path */
        var clipPath = new fabric.Rect({
          width: 300,
          height: 300,
          left: (canvas.getWidth() - 300) / 2,
          top: 10,
        });

        /* Creating a group of objects, in this case, a single rectangle */
        var group = new fabric.Group([
          new fabric.Rect({
            width: 100,
            height: 100,
            fill: "red",
            left: (canvas.getWidth() - 150) / 2,
            top: 10,
          }),
        ]);

        /* Applying the clip path to the canvas */
        canvas.clipPath = clipPath;

        /* Adding the group of objects to the canvas */
        canvas.add(group);
      })();
    </script>
  </body>
</html>


Solution

  • We want to show only the outside part of the image as semi-transparent.

    We can achieve a similar effect by using two overlapping objects:

    Basically, when we add any object, we will create a duplicate of that with the semi-transparent effect. so we will have two objects for each effect. 1) original 2) a duplicate of that with the semi-transparent effect.

    The idea is that when the image moves to the outside of the canvas, the original one will get clipped and the duplicate(semi-transparent) will show outside. To achieve this, we will update duplicate(semi-transparent) image position when the original image moves.

    Here, is a sample HTML code.

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Blur/Opacity Effect of Fabric.js Example</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>
      </head>
      <body>
        <canvas id="canvas" width="1200" height="500"></canvas>
        <style>
          body {
            margin: 0;
            overflow: hidden;
          }
        </style>
        <script>
          (function () {
            var canvas = new fabric.Canvas("canvas", {
              backgroundColor: "#f0f0f0",
              controlsAboveOverlay: true,
              selection: false, // Disable group selection
            });
    
            // Create a larger square (800x800) at the center of the canvas
            var largeSquare = new fabric.Rect({
              width: 800,
              height: 800,
              fill: "#ffffff", // Green color
              left: (canvas.getWidth() - 800) / 2,
              top: (canvas.getHeight() - 800) / 2,
              selectable: false,
              absolutePositioned: true,
            });
    
            // Create an array of smaller squares as movable objects
            var movableSquares = [
              // Initial squares...
              new fabric.Rect({
                width: 200,
                height: 200,
                fill: "red",
                left: (canvas.getWidth() - 200) / 2,
                top: (canvas.getHeight() - 200) / 2,
                opacity: 1, // Initial opacity
                hasControls: true,
                lockMovementX: false,
                lockMovementY: false,
              }),
            ];
    
            // Create an array of duplicates of the movable squares with opacity 0.5
            var movableSquareDups = movableSquares.map(function (square) {
              var dup = fabric.util.object.clone(square);
              dup.set({ opacity: 0.5 });
              return dup;
            });
    
            // Apply the clipPath to the original squares
            movableSquares.forEach(function (square) {
              square.clipPath = largeSquare;
            });
    
            // Add the larger square to the canvas
            canvas.add(largeSquare);
    
            // Add the duplicate squares to the canvas first, so they're behind the original squares
            movableSquareDups.forEach(function (square) {
              canvas.add(square);
            });
    
            // Add the smaller movable squares to the canvas
            movableSquares.forEach(function (square) {
              canvas.add(square);
            });
    
            var customEvtHandler = function (e) {
              var obj = e.target; // The object that's being moved or resized
    
              // Check if the object is one of the movable squares
              var index = movableSquares.indexOf(obj);
              if (index !== -1) {
                // Move and resize the corresponding duplicate square together with the original square
                movableSquareDups[index].set({
                  left: obj.left,
                  top: obj.top,
                  scaleX: obj.scaleX,
                  scaleY: obj.scaleY,
                  angle: obj.angle,
                  flipX: obj.flipX,
                  flipY: obj.flipY,
                });
    
                // Update the canvas display
                canvas.renderAll();
              }
            };
            // Add event listeners for moving, modifying, scaling, and rotating objects
            canvas.on("object:moving", customEvtHandler);
            canvas.on("object:modified", customEvtHandler);
            canvas.on("object:scaling", customEvtHandler);
            canvas.on("object:rotating", customEvtHandler);
          })();
        </script>
      </body>
    </html>