Fabric.js fillRule - evenodd (subtract shapes)

2019-09-07 02:57发布

I am trying to do hole in the polygon using fillRule = 'evenodd' in fabric.js

globalCompositeOperation won't work for me, because I need to see shape in the polygon hole like this

Here is my code: fiddle

There are 2 examples:

  • Fabric.js 2 polygons (doesn't work)
  • native html5 canvas 2 polygons (works)

When I set fillRule: 'evenodd' for fabric.js polygon, it is going to the fabric function and it's setting context fill to 'eveneodd'

_renderFill: function(ctx) {
   ...
      if (this.fillRule === 'evenodd') {
        ctx.fill('evenodd');
      }
      ...
      ctx.restore();
    },

But still it doesn't work. Please, give my some idea how to use 'enevodd' if fabric.js

1条回答
家丑人穷心不美
2楼-- · 2019-09-07 03:40

The 'evenodd' fill rule works fine with one fabric object. You cannot use this rule for separate objects and combine them together, even if you are using a group object. In a group rendering for the shapes happening separately, which means every object calls:

context.fill(); 

or

context.fill('evenodd')

Using 'evenodd' rule for multiple objects is missing feature in the Fabric.js.

Here is some workaround how to use 'evenodd' rule for one subjects polygon and multiple subtracted polygons.

Steps:

  1. Find closest point of the subtracted polygon to the last point of the subject polygon.
  2. Sort subtracted polygon arrays by closest index
  3. Combine Subject and subtracted polygons into one.

Here is a fiddle.

This code needs to be improved. It gives an idea how to make holes in the polygon using 'evenodd' rule.

var canvas = new fabric.Canvas('c', { selection: false });
var polygons = [];

var subjectPoly = [{x:30,y:10}, {x:300,y:50}, {x:20,y:290}];
var subtractPoly1 = [{x:50,y:50}, {x:200,y:140}, {x:220,y:40}];
var subtractPoly2 = [{x:10,y:10}, {x:300,y:200}, {x:60,y:150}];
var subtractPoly = [];
subtractPoly.push(subtractPoly1);
subtractPoly.push(subtractPoly2);

 var result = combinePolygonArrays(subjectPoly,sortPolygonArray(subtractPoly, closetPoint(subjectPoly[0],subtractPoly)));
  var poly1 = new fabric.Polygon(result, {
  	 fillRule: 'evenodd',
     fill: 'green',
     selectable: false      });      
  canvas.add( poly1);
var c= (new fabric.Circle({
    radius: 50,
    left: 125,
    top: 50,
    fill: 'red'
}));

canvas.add(c);
c.sendToBack();
  
function closetPoint(subjPolyPoint, subtrPoly){
	var minIndexes = [];
	for (var j in subtrPoly){
  	var minLength = Length(subjPolyPoint, subtrPoly[j][0]);
  	var	minIndex = 0;
    for (var i = 1; i < subtrPoly[j].length; i++){
      var newLength = Length(subjPolyPoint, subtrPoly[j][i])
        if (minLength > newLength){
          minLength = newLength;
          minIndex = i;
        }
    }
    minIndexes.push({arrayIndex: j, elementIndex: minIndex});
  }
    return minIndexes;
}

function Length(point1, point2) {      
    var x1 = point1.x,
        x2 = point2.x,
        y1 = point1.y,
        y2 = point2.y;
		    return Math.sqrt((x1 -= x2) * x1 + (y1 -= y2) *y1);
}

function sortPolygonArray(array, indexes){
var result = [];
	for (var i in indexes){
  	var newArray = [];
  	var originalLength = array[indexes[i].arrayIndex].length;
  	while (newArray.length != originalLength){
      if (indexes[i].elementIndex == originalLength){
        indexes[i].elementIndex = 0;
      }
   	 newArray.push(array[indexes[i].arrayIndex][indexes[i].elementIndex]);
   	 indexes[i].elementIndex++;
 	 }
   result.push(newArray);
  }
	
  return result;
}

function combinePolygonArrays(subjPoly, subtrPoly){
	var newArray = subjPoly;
  var lastSubjectPoint = subjPoly[subjPoly.length - 1];
  for (var i in subtrPoly){
  	var firstSubtractedPoint = subtrPoly[i][0];
    newArray = newArray.concat(subtrPoly[i]);
  	newArray.push(firstSubtractedPoint);
 		newArray.push(lastSubjectPoint);
  }
  return newArray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.11/fabric.js"></script>
<canvas id="c" width="500" height="350" style="border:1px solid #ccc"></canvas>

查看更多
登录 后发表回答