Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fabricjs detect mouse over object path

Is it possible to capture object:over in Fabric.js only if mouse is on the shape itself and not on the imaginary square that contains it?

I have a jsFiddle demo, which contains an U shape. You can see that even if i have the pointer inside the U and not touching any of the lines, it still detects it as a object:over event.

enter image description here

Javascript:

var canvas = new fabric.Canvas("c1", {
    isDrawingMode: false
});

canvas.loadFromJSON(objectsJson, function () {
    canvas.renderAll();
});

canvas.on("object:over", function () {
    console.log("object over");
});
canvas.on("object:out", function () {
    console.log("object out");
});

// code to capture mouse over object while isDrawingMode = false
canvas.findTarget = (function (originalFn) {
    return function () {
        var target = originalFn.apply(this, arguments);
        if (target) {
            if (this._hoveredTarget !== target) {
                canvas.fire('object:over', { target: target });
                if (this._hoveredTarget) {
                    canvas.fire('object:out', { target: this._hoveredTarget });
                }
                this._hoveredTarget = target;
            }
        }
        else if (this._hoveredTarget) {
            canvas.fire('object:out', { target: this._hoveredTarget });
            this._hoveredTarget = null;
        }
        return target;
    };
})(canvas.findTarget);
like image 762
Catalin Avatar asked Feb 02 '14 13:02

Catalin


1 Answers

In fabric.js, for each shape there would created virtual box and when you intersect with this virtual box, the binded event is triggered.

During the create of any shape on fabric.js, this below function create the virtual box by creating various points eg: top left, top right, bottom left, bottom right, middle left, middle right, middle bottom and middle top.

setCoords: function() {
   var strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0,
   padding = this.padding,
   theta = degreesToRadians(this.angle);

   this.currentWidth = (this.width + strokeWidth) * this.scaleX + padding * 2;
   this.currentHeight = (this.height + strokeWidth) * this.scaleY + padding * 2;

   // If width is negative, make postive. Fixes path selection issue
   if (this.currentWidth < 0) {
    this.currentWidth = Math.abs(this.currentWidth);
   }

   var _hypotenuse = Math.sqrt(
    Math.pow(this.currentWidth / 2, 2) +
    Math.pow(this.currentHeight / 2, 2));

   var _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0);

   // offset added for rotate and scale actions
   var offsetX = Math.cos(_angle + theta) * _hypotenuse,
      offsetY = Math.sin(_angle + theta) * _hypotenuse,
      sinTh = Math.sin(theta),
      cosTh = Math.cos(theta);

   var coords = this.getCenterPoint();
   var tl = {
    x: coords.x - offsetX,
    y: coords.y - offsetY
   };
   var tr = {
    x: tl.x + (this.currentWidth * cosTh),
    y: tl.y + (this.currentWidth * sinTh)
   };
   var br = {
    x: tr.x - (this.currentHeight * sinTh),
    y: tr.y + (this.currentHeight * cosTh)
   };
   var bl = {
    x: tl.x - (this.currentHeight * sinTh),
    y: tl.y + (this.currentHeight * cosTh)
   };
   var ml = {
    x: tl.x - (this.currentHeight/2 * sinTh),
    y: tl.y + (this.currentHeight/2 * cosTh)
   };
   var mt = {
    x: tl.x + (this.currentWidth/2 * cosTh),
    y: tl.y + (this.currentWidth/2 * sinTh)
   };
   var mr = {
    x: tr.x - (this.currentHeight/2 * sinTh),
    y: tr.y + (this.currentHeight/2 * cosTh)
   };
   var mb = {
    x: bl.x + (this.currentWidth/2 * cosTh),
    y: bl.y + (this.currentWidth/2 * sinTh)
   };
   var mtr = {
    x: mt.x,
    y: mt.y
   };
  this.oCoords = {
    // corners
    tl: tl, tr: tr, br: br, bl: bl,
    // middle
    ml: ml, mt: mt, mr: mr, mb: mb,
    // rotating point
    mtr: mtr
   };

   // set coordinates of the draggable boxes in the corners used to scale/rotate the image
   this._setCornerCoords && this._setCornerCoords();

   return this;
}

So whenever you intersect with any of these 8 points by mouse, the attached event would be fired.

As I know fabric.js, it does not provide the functionality you wanted.

UPDATE:-

As RaraituL said, the pixel detection is possible through perPixelTargetFind(), you can get the example on here. http://fabricjs.com/per-pixel-drag-drop/

like image 130
Suman Bogati Avatar answered Oct 20 '22 12:10

Suman Bogati