Fabricjs 按颜色隔离对象计数

Fabricjs isolate object count by color

我有一个计数器,用于计算 canvas 上的对象总数,但我想将其隔离以按对象颜色计数。 canvas.getObjects().length; 可以按颜色获取对象吗?

var canvas = new fabric.Canvas('c');
var iconTriangle = new fabric.Triangle({
  width: 62.5,
  height: 50,
  originX: 'center',
  originY: 'center',
  fill: "green"
});
icon = iconTriangle;
canvas.add(icon);
//disable icon & hide when hovering over existing icon
canvas.on('mouse:over', function(obj) {
  iconTriangle.set('opacity', 0);
  icon = null;
  canvas.renderAll()
});
//restor icon & unhide
canvas.on('mouse:out', function(obj) {
  iconTriangle.set('opacity', 1);
  icon = iconTriangle;
  canvas.renderAll()
});

canvas.on('mouse:move', function(obj) {
  icon.top = obj.e.y - 80;
  icon.left = obj.e.x - 10;
  canvas.renderAll()
});
canvas.on('mouse:out', function(obj) {
  icon.top = -100;
  icon.left = -100;
  canvas.renderAll()
});
//place icon and count each by type
canvas.on('mouse:up', function(obj) {
  var count = canvas.getObjects().length;
  document.getElementById("greentally").value = count;
  document.getElementById("yellowtally").value = count;
  document.getElementById("redtally").value = count;
  canvas.add(icon.clone());
  canvas.renderAll();
});
//set icon type
function iconSet() {
  if (document.getElementById("green").checked == true) {
    iconTriangle.setFill("green");
    canvas.renderAll();
    canvas.trigger('object:modified', {
      target: iconTriangle
    });

  } else if (document.getElementById("yellow").checked == true) {
    iconTriangle.setFill("yellow");
    canvas.renderAll();
    canvas.trigger('object:modified', {
      target: iconTriangle
    });
  } else if (document.getElementById("red").checked == true) {
    iconTriangle.setFill("red");
    canvas.renderAll();
    canvas.trigger('object:modified', {
      target: iconTriangle
    });
  }
}
canvas {
  border: 1px solid #ccc;
}

.tally {
  position: fixed;
  width: 50px;
  left: 255px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.js"></script>

<input name="iconType" type="radio" id="green" onclick="iconSet()" />Green &emsp;
<input class="tally" disabled id="greentally" type="text" value="0">
<br>
<input name="iconType" type="radio" id="yellow" onclick="iconSet()" /> Yellow &emsp;
<input class="tally" disabled id="yellowtally" type="text" value="0">
<br>
<input name="iconType" type="radio" id="red" onclick="iconSet()" /> red &emsp;
<input class="tally" disabled id="redtally" type="text" value="0">
<canvas id="c" width="300" height="300"></canvas>

使用自定义辅助函数,我能够按颜色进行计数和过滤。您可以扩展功能以处理更复杂的过滤器,例如形状类型和颜色。此外,您可能希望实现缓存机制以防止它每次都重新计数。

     function canvasCount(canvas) {
          var obj = canvas.getObjects();
          var res = {};

          for (var i = 0; i < obj.length; i++) {
                if (!obj[i].type) continue; // skip main canvas obj 

                // get the shapes color
                var color = obj[i].fill;

                // adds color key if not already in obj
                if (!res[color]) res[color] = 0;
                // increments color each by one
                res[color]++;

                // Length of all yellow triangles 
                // res.yellow
          }
          return res;
    }

var canvas = new fabric.Canvas('c');
var iconTriangle = new fabric.Triangle({
  width: 62.5,
  height: 50,
  originX: 'center',
  originY: 'center',
  fill: "green"
});
icon = iconTriangle;
canvas.add(icon);
//disable icon & hide when hovering over existing icon
canvas.on('mouse:over', function(obj) {
  iconTriangle.set('opacity', 0);
  icon = null;
  canvas.renderAll()
});
//restor icon & unhide
canvas.on('mouse:out', function(obj) {
  iconTriangle.set('opacity', 1);
  icon = iconTriangle;
  canvas.renderAll()
});

canvas.on('mouse:move', function(obj) {
  icon.top = obj.e.y - 80 ;
  icon.left = obj.e.x - 10 ;
  canvas.renderAll()
});
canvas.on('mouse:out', function(obj) {
  icon.top = -100;
  icon.left = -100;
  canvas.renderAll()
});
//place icon and count each by type
canvas.on('mouse:up', function(obj) {
  var count = canvasCount(canvas);
  
  
  document.getElementById("greentally").value = count.green || 0;
  document.getElementById("yellowtally").value = count.yellow || 0 ;
  document.getElementById("redtally").value = count.red || 0;
  canvas.add(icon.clone());
  canvas.renderAll();
});
//set icon type
function iconSet() {
  if (document.getElementById("green").checked == true) {
    iconTriangle.setFill("green");
    canvas.renderAll();
    canvas.trigger('object:modified', {
      target: iconTriangle
    });
  } else if (document.getElementById("yellow").checked == true) {
    iconTriangle.setFill("yellow");
    canvas.renderAll();
    canvas.trigger('object:modified', {
      target: iconTriangle
    });
  } else if (document.getElementById("red").checked == true) {
    iconTriangle.setFill("red");
    canvas.renderAll();
    canvas.trigger('object:modified', {
      target: iconTriangle
    });
  }
}

function canvasCount(canvas) {
      var obj = canvas.getObjects();
      var res = {};

      for (var i = 0; i < obj.length; i++) {
            if (!obj[i].type) continue; // skip main canvas obj 

            // get the shapes color
            var color = obj[i].fill;
            
            // adds color key if not already in obj
            if (!res[color]) res[color] = 0;
            // increments color each by one
            res[color]++;

            // Length of all yellow triangles 
            // res.yellow

         
      }
      return res;
}
canvas {
  border: 1px solid #ccc;
}

.tally {
  position: fixed;
  width: 50px;
  left: 255px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.js"></script>

<input name="iconType" type="radio" id="green" onclick="iconSet()" />Green &emsp;
<input class="tally" disabled id="greentally" type="text" value="0">
<br>
<input name="iconType" type="radio" id="yellow" onclick="iconSet()" /> Yellow &emsp;
<input class="tally" disabled id="yellowtally" type="text" value="0">
<br>
<input name="iconType" type="radio" id="red" onclick="iconSet()" /> red &emsp;
<input class="tally" disabled id="redtally" type="text" value="0">
<canvas id="c" width="300" height="300"></canvas>

注意::我无法修复 Uncaught TypeError: Cannot set property 'top' of null 错误。

计算具有特定颜色(或任何其他 属性)的对象很容易,因为您通过 canvas.getObjects() 获得 canvas 上所有对象的数组 - 然后您只需过滤那些您正在通过 filter() 寻找。为此,我在我的代码片段中引入了一个 getObjectsBy(fn) 辅助函数。

也就是说,您的代码还有其他几个值得解决的问题:

  • 制作 iconiconTriangle 的复制引用乍一看可能看起来很整洁,但实际上毫无意义且容易出错,例如您必须编写单独的逻辑来隐藏副本,并且在实际需要时必须注意它是否 null 。这就是您获得这些 Cannot set property 'top' of null 的原因。在我的代码片段中,我通过类似工厂的 addIcon() 创建了一个新图标,它与 clone() 一样易于使用,但也更易于维护。
  • mouse:up 事件不仅会在您单击 canvas 时触发,还会在您单击对象时触发,因此每次移动时都会创建不必要的图标。在我的代码片段中,我在 mouse:up 侦听器中使用早期的 return 来解决此问题。
  • iconSet() 似乎是引入辅助函数以使其更简洁的好地方。此外,将颜色存储在单独的变量中有助于将图标创建与 iconTriangle.
  • 分离

var canvas = new fabric.Canvas('c');
var currentColor;
var defaultIcon = {
  width: 62.5,
  height: 50,
  originX: 'center',
  originY: 'center'
};
var iconTriangle = new fabric.Triangle(defaultIcon);
setColor('green');
canvas.add(iconTriangle);
//disable icon & hide when hovering over existing icon
canvas.on('mouse:over', function(obj) {
  iconTriangle.set('opacity', 0);
  canvas.renderAll();
});
//restor icon & unhide
canvas.on('mouse:out', function(e) {
  // if 'target' is null, means mouse is out of canvas
  if (e.target) {
    iconTriangle.set('opacity', 1);
  } else {
    iconTriangle.left = -100;
    iconTriangle.top = -100;
  }
  canvas.renderAll();
});
//move pointer icon
canvas.on('mouse:move', function(obj) {
  iconTriangle.top = obj.e.y - 80;
  iconTriangle.left = obj.e.x - 10;
  canvas.renderAll();
});

//count each by type and place new icon
canvas.on('mouse:up', function(e) {
  if (e.target) {
    return
  }
  var red = getObjectsBy((obj) => obj.fill === 'red').length;
  var green = getObjectsBy((obj) => obj.fill === 'green').length;
  var yellow = getObjectsBy((obj) => obj.fill === 'yellow').length;
  document.getElementById("greentally").value = green;
  document.getElementById("yellowtally").value = yellow;
  document.getElementById("redtally").value = red;
  addIcon(e.e.x - 10, e.e.y - 80, currentColor);
});
//set icon type
function iconSet() {
  if (document.getElementById("green").checked == true) {
    setColor('green');
  } else if (document.getElementById("yellow").checked == true) {
    setColor('yellow');
  } else if (document.getElementById("red").checked == true) {
    setColor('red');
  }
}
function setColor (color) {
  currentColor = color;
  iconTriangle.setFill(currentColor);
  canvas.renderAll();
}
function getObjectsBy (fn) {
  return canvas.getObjects().filter(fn)
}
function addIcon (x, y, color) {
  var icon = new fabric.Triangle(defaultIcon);
  icon.setFill(color);
  icon.left = x;
  icon.top = y;
  canvas.add(icon);
  canvas.renderAll();
}
canvas {
  border: 1px solid #ccc;
}

.tally {
  position: fixed;
  width: 50px;
  left: 255px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.js"></script>

<input checked name="iconType" type="radio" id="green" onclick="iconSet()" />Green &emsp;
<input class="tally" disabled id="greentally" type="text" value="0">
<br>
<input name="iconType" type="radio" id="yellow" onclick="iconSet()" /> Yellow &emsp;
<input class="tally" disabled id="yellowtally" type="text" value="0">
<br>
<input name="iconType" type="radio" id="red" onclick="iconSet()" /> red &emsp;
<input class="tally" disabled id="redtally" type="text" value="0">
<canvas id="c" width="300" height="300"></canvas>