Fabric.js: 灰色除所选部分以外的图像其他部分

Fabric.js: Grayout other portion of image other than the selected portion

在下图中,canvas 中有三个框,图像​​底部有三个按钮。每当我单击一个按钮时,canvas 中的相应对象就会被选中(即,当我单击绿色按钮时,canvas 中的绿色矩形被选中)。

我的要求是仅突出显示所选部分,canvas 的其他部分应显示为灰色。 (例如:如果我点击绿色按钮,应该选择绿色矩形,其他部分应该用灰色背景覆盖)。

Js Fiddle Link: https://jsfiddle.net/rnvs2hdk/1/

var canvas = new fabric.Canvas('c');
canvas.backgroundColor = 'yellow';
var li= []

canvas.renderAll();

fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function(myImg) {
 var img1 = myImg.set({ left: 0, top: 0 ,width:400,height:500});
 canvas.add(img1); 
 var green = new fabric.Rect({
       left: 50,
       top: 50,
       width: 50,
       height: 50,
       fill: 'rgba(255,255,255,1)',
       stroke: 'rgba(34,177,76,1)',
       strokeWidth: 5,
       name:"green"
  });
  var yellow = new fabric.Rect({
       left: 150,
       top: 50,
       width: 50,
       height: 50,
       fill: 'rgba(255,255,255,1)',
       stroke: 'rgba(255,255,0,1)',
       strokeWidth: 5,
       name:"yellow"
  });
  var red = new fabric.Rect({
       left: 250,
       top: 50,
       width: 50,
       height: 50,
       fill: 'rgba(255,255,255,1)',
       stroke: 'rgba(255,0,0,1)',
       strokeWidth: 5,
       name:"red"
  });
   canvas.add(green, yellow,red);
   li.push(green);
   li.push(yellow);
   li.push(red);
   li.some(v=>{
     var btn = document.createElement("BUTTON");   // Create a <button> elem
         btn.innerHTML = v.name;     
     btn.addEventListener('click',function(e){
        var name = e.target
      if(name.innerText == "green"){
        canvas.setActiveObject(li[0]);
      }
      if(name.innerText == "yellow"){
        canvas.setActiveObject(li[1]);
      }
      if(name.innerText == "red"){
        canvas.setActiveObject(li[2]);
      }
     });// Insert text
            document.body.appendChild(btn);  
   });
   console.log(li);
});


预期结果:(示例)

我已经完成了在实际矩形周围再创建 4 个矩形的操作。我已经将外部矩形变灰,因此它具有叠加效果。另外,我会在创建新矩形之前删除所有矩形。

let blankColor = "rgba(0,0,0,0)";
let grayOut = "rgba(0,0,0,0.4)";
let rect = createRect(x, y, width, height, 1, blankColor);
let rect1 = createRect(0, 0, getViewPortDimensions()[0], y, 0, grayOut);
let rect2 = createRect(0, y + height, getViewPortDimensions()[0], getViewPortDimensions()[1] - (y + height), 0, grayOut);
let rect3 = createRect(0, y, x, height, 0, grayOut);
let rect4 = createRect(x + width, y, getViewPortDimensions()[0] - (x + width), height, 0, grayOut);
state.canvas.add(rect, rect1, rect2, rect3, rect4);

删除已绘制的矩形:

state.canvas.forEachObject((o, index) => {
  if (index != 0) {
    state.canvas.remove(o);
  }
})

这是我的解决方案。使用 after:render 事件,您可以在渲染后对每一帧执行 canvas 绘制操作。这种方法的好处是避免了必须根据需要创建和销毁织物对象,这是一项昂贵的操作。

请务必在缩放和移动等操作期间调用 .setCoords() 方法,以便对象在执行这些操作时更新其位置信息。

var canvas = new fabric.Canvas('c');
canvas.backgroundColor = 'yellow';
var li = [];

canvas.renderAll();

fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function(myImg) {
  var img1 = myImg.set({
    left: 0,
    top: 0,
    width: 400,
    height: 500
  });
  canvas.add(img1);
  var green = new fabric.Rect({
    left: 50,
    top: 50,
    width: 50,
    height: 50,
    fill: 'rgba(255,255,255,1)',
    stroke: 'rgba(34,177,76,1)',
    strokeWidth: 5,
    name: "green",
    hasRotatingPoint: false
  });
  var yellow = new fabric.Rect({
    left: 150,
    top: 50,
    width: 50,
    height: 50,
    fill: 'rgba(255,255,255,1)',
    stroke: 'rgba(255,255,0,1)',
    strokeWidth: 5,
    name: "yellow",
    hasRotatingPoint: false
  });
  var red = new fabric.Rect({
    left: 250,
    top: 50,
    width: 50,
    height: 50,
    fill: 'rgba(255,255,255,1)',
    stroke: 'rgba(255,0,0,1)',
    strokeWidth: 5,
    name: "red",
    hasRotatingPoint: false
  });
  canvas.add(green, yellow, red);
  li.push(green);
  li.push(yellow);
  li.push(red);
  li.some(v => {
    var btn = document.createElement("BUTTON"); // Create a <button> elem
    btn.innerHTML = v.name;
    btn.addEventListener('click', function(e) {
      var name = e.target
      if (name.innerText == "green") {
        canvas.setActiveObject(li[0]);
      }
      if (name.innerText == "yellow") {
        canvas.setActiveObject(li[1]);
      }
      if (name.innerText == "red") {
        canvas.setActiveObject(li[2]);
      }
    }); // Insert text
    document.body.appendChild(btn);
  });
  console.log(li);
});

canvas.on({
  'object:moving': function(e) {
    //makes objects update their coordinates while being moved
    e.target.setCoords();
  },
  'object:scaling': function(e) {
    //makes objects update their coordinates while being scaled
    e.target.setCoords();
  }
});

//the after:render event allows you to perform a draw function on each frame after it is rendered
canvas.on('after:render', function() {

  var ctx = canvas.contextContainer,
    obj = canvas.getActiveObject();

  if (obj) {
    //set the fill color of the overlay
    ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
    var bound = obj.getBoundingRect();
    ctx.beginPath();
    //draw rectangle to the left of the selection
    ctx.rect(0, 0, bound.left, canvas.height);
    //draw rectangle to the right of the selection
    ctx.rect(bound.left + bound.width, 0, canvas.width - bound.left - bound.width, canvas.height);
    //draw rectangle above the selection
    ctx.rect(bound.left, 0, bound.width, bound.top);
    //draw rectangle below the selection
    ctx.rect(bound.left, bound.top + bound.height, bound.width, canvas.height - bound.top - bound.height)
    ctx.fill();
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<canvas id="c" width="400" height="400"></canvas>
<div id="bt"></div>