0

I'm working on a project where images can be added to a FabricJS canvas at a variety of resolutions. Because of how Fabric handles stroke widths, this variance in image sizes causes strokes to be inconsistently rendered.

Borrowing from byoungb's solution for maintaining a constant stroke width, I've managed to get my image borders to remain both visually consistent, and have correct control/bounding positions by overriding the _renderStroke and _getTransformedDimensions methods of the Image class. This breaks down however in cases where the image is skewed and I can't seem to nail down the correct place to modify the object dimension calculation in cases where skewing is present (I expect I need to modify the _calcDimensionsTransformMatrix method for Image but have had no success so far).

http://jsfiddle.net/melchiar/bt4ckmea/ (bounding rectangle shown in black) Constant Image Stroke

//override stroke rendering for constant stroke width independent of scaling
fabric.Image.prototype._renderStroke = function(ctx) {
  if (!this.stroke || this.strokeWidth === 0) {
    return;
  }
  if (this.shadow && !this.shadow.affectStroke) {
    this._removeShadow(ctx);
  }
  ctx.save();
  ctx.scale(1 / this.scaleX, 1 / this.scaleY);
  this._setLineDash(ctx, this.strokeDashArray, this._renderDashedStroke);
  this._applyPatternGradientTransform(ctx, this.stroke);
  ctx.stroke();
  ctx.restore();
};

//modify method for calculating control and bounding box positions
fabric.Image.prototype._getTransformedDimensions = function(skewX, skewY) {
  if (typeof skewX === 'undefined') {
    skewX = this.skewX;
  }
  if (typeof skewY === 'undefined') {
    skewY = this.skewY;
  }
  var dimensions = this._getNonTransformedDimensions();
  if (skewX === 0 && skewY === 0) {
    return {
      x: dimensions.x * this.scaleX + (this.strokeWidth * (1 - this.scaleX)),
      y: dimensions.y * this.scaleY + (this.strokeWidth * (1 - this.scaleY))
    };
  }
  var dimX = dimensions.x / 2,
    dimY = dimensions.y / 2,
    points = [{
        x: -dimX,
        y: -dimY
      },
      {
        x: dimX,
        y: -dimY
      },
      {
        x: -dimX,
        y: dimY
      },
      {
        x: dimX,
        y: dimY
      }
    ],
    i, transformMatrix = this._calcDimensionsTransformMatrix(skewX, skewY, false),
    bbox;
  for (i = 0; i < points.length; i++) {
    points[i] = fabric.util.transformPoint(points[i], transformMatrix);
  }
  bbox = fabric.util.makeBoundingBoxFromPoints(points);
  return {
    x: bbox.width,
    y: bbox.height
  };
};
melchiar
  • 2,782
  • 16
  • 27

1 Answers1

1

I ended up finding the solution. The bounding box size of skewed objects with modified stroke can be fixed by modifying the bbox values being returned at the end of the _getTransformedDimensions method.

http://jsfiddle.net/melchiar/6Ljguz7k/

//override stroke rendering for constant stroke width independent of scaling
fabric.Image.prototype._renderStroke = function(ctx) {
  if (!this.stroke || this.strokeWidth === 0) {
    return;
  }
  if (this.shadow && !this.shadow.affectStroke) {
    this._removeShadow(ctx);
  }
  ctx.save();
  ctx.scale(1 / this.scaleX, 1 / this.scaleY);
  this._setLineDash(ctx, this.strokeDashArray, this._renderDashedStroke);
  this._applyPatternGradientTransform(ctx, this.stroke);
  ctx.stroke();
  ctx.restore();
};

//modify method for calculating control and bounding box positions
fabric.Image.prototype._getTransformedDimensions = function(skewX, skewY) {
  if (typeof skewX === 'undefined') {
    skewX = this.skewX;
  }
  if (typeof skewY === 'undefined') {
    skewY = this.skewY;
  }
  var dimensions = this._getNonTransformedDimensions();
  if (skewX === 0 && skewY === 0) {
    return {
      x: dimensions.x * this.scaleX + (this.strokeWidth * (1 - this.scaleX)),
      y: dimensions.y * this.scaleY + (this.strokeWidth * (1 - this.scaleY))
    };
  }
  var dimX = dimensions.x / 2,
    dimY = dimensions.y / 2,
    points = [{
        x: -dimX,
        y: -dimY
      },
      {
        x: dimX,
        y: -dimY
      },
      {
        x: -dimX,
        y: dimY
      },
      {
        x: dimX,
        y: dimY
      }
    ],
    i, transformMatrix = this._calcDimensionsTransformMatrix(skewX, skewY, false),
    bbox;
  for (i = 0; i < points.length; i++) {
    points[i] = fabric.util.transformPoint(points[i], transformMatrix);
  }
  bbox = fabric.util.makeBoundingBoxFromPoints(points);
  return {
    x: bbox.width + (this.strokeWidth * (1 - this.scaleX)),
    y: bbox.height + (this.strokeWidth * (1 - this.scaleY))
  };
};
melchiar
  • 2,782
  • 16
  • 27