How to clip image by another image in Konva like fabric.js clipPath it do

95 Views Asked by At

How can i clip image by another image? I need behavoior like in fabric.js clipPath - one image clip another. Main image will be aditable - draggable and scalable, clip image will be static. Main Image can contain video

I try [https://jsfiddle.net/mgdah8wc/23/] but after adding group with clipped image all shapes under it wi bell clipped to. Make like [https://jsfiddle.net/xLL3veu6/] i cant becose i have several images with clipping

1

There are 1 best solutions below

2
Vanquished Wombat On

If the aim is to use (a) the giraffe image as a mask to make a path to be filled with (b) the grey image then have them both appear above (c) the orange rect then you need to compose the a + b interaction to a separate canvas and grab the image off that into the canvas. See snippet below.

Konva is a wrapper for the HTML5 canvas and as such it is subject to the same compositing rules as the canvas. It is likely that fabric uses this intermediate canvas compositing approach too under the covers.

EDIT: Someone mentioned that using another stage was not the optimum solution. They proposed using a group. However the issue here is that the compositing setting covers the entire canvas - and a group is drawn into the layer, which in Konva is itself a canvas. What I mean by this is that the destination-in operates over the entire layer that the group is located on, so that wipes out image of the the prior shapes that has been built up on the layer. It is possible to use another layer in the same stage because in Konva each layer is a canvas.

First demo: using a hidden canvas.

// Create a Konva.js stage
const stage = new Konva.Stage({
  container: 'canvas-container',
  width: 600,
  height: 600,
});

// Create a Konva.js layer
const layer = new Konva.Layer();
stage.add(layer);

const rect = new Konva.Rect({
  x: 0,
  y: 0,
  width: 300,
  height: 300,
  draggable: true,
  fill: 'cyan',
});
layer.add(rect)


// make a function to receive the image of the canvas
let applyImg = function(image) {
  layer.add(image)
}
makeClipped("https://assets.codepen.io/255591/greybox.png", 'https://assets.codepen.io/255591/giraffe.png', applyImg)

/*
Function to laod given images into an offscreen stage and return a Konva.Image containing that image 
*/
function makeClipped(url1, url2, callbackFn) {

  // Make the offscreen stage. The container ele of the stage has display=none.
  const stage = new Konva.Stage({
    container: 'canvas-container2'
  });

  // Create a Konva.js layer
  const layer = new Konva.Layer();
  stage.add(layer);

  // make the background image from the given url
  Konva.Image.fromURL(url1, (konvaImage) => {
    konvaImage.setAttrs({
      x: 10,
      y: 10,
      draggable: true,
    });
    layer.add(konvaImage)

    // Make the mask from the foreground image - note this is in the callback from the load of image1
    Konva.Image.fromURL(url2, (konvaImage) => {
      konvaImage.setAttrs({
        x: 10,
        y: 10,
        draggable: true,
        globalCompositeOperation: 'destination-in' // use this comp op to hide all of background image outside of the foreground image.
      });

      // add this image to the layer
      layer.add(konvaImage);

      // make a Konva image from the layer - note this is in the callback from the load of image2
      Konva.Image.fromURL(layer.toDataURL(), (konvaImage) => {
        callbackFn(konvaImage)

        // Clean up the resources that we used.
        stage.destroy()
      })
    });
  });
}
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #f0f0f0;
}

#canvas-container2 {
  display: none;
}

.container {
  display: inline-block;
}
<html>

<head>
  <script src="https://unpkg.com/[email protected]/konva.min.js"></script>
  <meta charset="utf-8" />
  <title>o</title>

</head>

<body>
  <div id="canvas-container" class='container'></div>
  <div id="canvas-container2" class='container'></div>

</body>

</html>

Second demo: using additional layer on the original stage.

 
// Create a Konva.js stage
const stage = new Konva.Stage({
  container: 'canvas-container',
  width: 600,
  height: 600,
});

// Create a Konva.js layer
const layer = new Konva.Layer();
stage.add(layer);

const rect = new Konva.Rect({
  x: 0,
  y: 0,
  width: 300,
  height: 300,
  draggable: true,
  fill: 'cyan',
});
layer.add(rect)

 
  const layer2 = new Konva.Layer()
  stage.add(layer2)

  // make the background image from the given url
  Konva.Image.fromURL("https://assets.codepen.io/255591/gradientbg.png", (konvaImage) => {
   layer2.add(konvaImage)
    
    // Make the mask from the foreground image - note this is in the callback from the load of image1
    Konva.Image.fromURL("https://assets.codepen.io/255591/giraffe.png", (konvaImage) => {
      konvaImage.setAttrs({
       globalCompositeOperation: 'destination-in', // use this comp op to hide all of background image outside of the foreground image.
        draggable: true
      });

      // add this image to the layer
      layer2.add(konvaImage);

    });    
  });   
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #f0f0f0;
}
.container {
  display: inline-block;
}
<head>
    <script src="https://unpkg.com/[email protected]/konva.min.js"></script>
    <meta charset="utf-8" />
    <title>o</title>
 
  </head>

  <body>
    <div id="canvas-container" class='container'></div>
  </body>