5


I want to show object bounding box when mouse hover over objects like this video, how to do that ?
enter image description here

I'm using canvas.on('mouse:over') with selectedObj.drawBorders function. However, outline box is drawn in not correct position. And I don't know how to clear that outline box when mouse moves out of the object.

Here is my code:

$(function() {
  
  var canvasObject = document.getElementById("editorCanvas");

  // set canvas equal size with div
  $(canvasObject).width($("#canvasContainer").width());
  $(canvasObject).height($("#canvasContainer").height());

  var canvas = new fabric.Canvas('editorCanvas', {
   backgroundColor: 'white',
   selectionLineWidth: 2,
   width: $("#canvasContainer").width(),
   height: $("#canvasContainer").height()
  });
  
  canvas.viewportTransform[4] = 20;
     canvas.viewportTransform[5] = 40;
  
  canvas.on('mouse:over', function(opts) {
   var selectedObj = opts.target;
   if (selectedObj != null) {
    selectedObj.drawBorders(canvas.getContext())
   }
  });
  
  var text = new fabric.Text('hello world', { left: 50, top: 50 });
  canvas.add(text);
  
  setObjectCoords();
  
  function setObjectCoords() {
    canvas.forEachObject(function(object) {
   object.setCoords();
    });
  }
  
 });
<style>
  #canvasContainer {
    width: 100%;
    height: 100vh;
    background-color: gray;
  }
</style>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
  
<div id="canvasContainer">
  <canvas id="editorCanvas"></canvas>
</div>

Please help me resolve this problem!
Thank you!

Tho Bui Ngoc
  • 763
  • 1
  • 11
  • 36

4 Answers4

12

Use method _renderControls and in styleOverride set hasControls : false to draw only borders.

DEMO

$(function() {
  var canvas = new fabric.Canvas('editorCanvas', {
    backgroundColor: 'white',
    selectionLineWidth: 2,
    width: $("#canvasContainer").width(),
    height: $("#canvasContainer").height()
  });

  var text = new fabric.IText('hello world', {
    left: 50,
    top: 50
  });
  canvas.add(text);
  canvas
  text.on('mouseover', function() {
    this._renderControls(this.canvas.contextTop, {
      hasControls: false
    })
  })
  text.on('mousedown', function() {
    this.canvas.clearContext(this.canvas.contextTop);
  })
  text.on('mouseout', function() {
    this.canvas.clearContext(this.canvas.contextTop);
  })
});
<style>
  #canvasContainer {
    width: 100%;
    height: 100vh;
    background-color: gray;
  }
</style>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
  
<div id="canvasContainer">
  <canvas id="editorCanvas"></canvas>
</div>
Durga
  • 15,263
  • 2
  • 28
  • 52
3

fabricjs@4.5.1

It work for me, like this:

// mouse:over draw bound box
this._canvas.on('mouse:over', (evet) => {
  const { target } = evet;
  if (this._canvas.getActiveObjects().length) {
    // skip group hover
    return;
  }

  // skip group hover
  if (target instanceof fabric.Object && !(target instanceof Array)) {
    const bound = target.getBoundingRect();
    const ctx = this._canvas.getContext();
    ctx.strokeStyle = 'red';
    ctx.strokeRect(
      bound.left,
      bound.top,
      bound.width,
      bound.height
    );
  }
});
// mouse:out remove bound box
this._canvas.on('mouse:out', (evet) => {
  const { target } = evet;
  if (this._canvas.getActiveObjects().length) {
    return;
  }

  // skipp group hover
  if (target instanceof fabric.Object && !(target instanceof Array)) {
     this._canvas.renderAll(); // render all, will clear bounds box drawed by mouse:over
  }
});
fanlion
  • 51
  • 1
2

This is how I would do it using plain Javascript: I measure the text using the ctx.measureText() method. The function drawBBox() is drawing the bounding box with no stroke On mousemove over the canvas I detect the position of the mouse and if the mouse is inside the box I call ctx.stroke().

Please read the comments in my code.

I hope you'll find this useful although I'm not using fabricjs

let ctx = editorCanvas.getContext("2d");
let cw = editorCanvas.width = canvasContainer.clientWidth,cx = cw/2;
let ch = editorCanvas.height = canvasContainer.clientHeight,cy = ch/2;
let start = {x:50,y:50}// where the text begin


let text = "hello world"; 
//set some properties of the text
ctx.fillStyle = "blue";
ctx.font="2em Verdana";
ctx.textBaseline="hanging";
//draw the text
ctx.fillText(text,start.x,start.y);

let measure = ctx.measureText(text);

function drawBBox(measure){
// a function to draw the bounding box
// the box has no stroke yet
  ctx.beginPath();
  ctx.moveTo(start.x,start.y)
  ctx.lineTo(start.x+measure.width,start.y);
  ctx.lineTo(start.x+measure.width,start.y+36);//36 = 2em: the height of the text
  ctx.lineTo(start.x,start.y+36);
  ctx.closePath();
}



editorCanvas.addEventListener("mousemove",(evt)=>{
  //clear the canvas
  ctx.clearRect(0,0,cw,ch);
  //get the position of the mouse
  let m = oMousePos(editorCanvas, evt);
  //draw the text
  ctx.fillText(text,start.x,start.y);
  // draw the bounding box with no stroke
  drawBBox(measure);
  
  // if the mouse is inside the bounding box apply the stroke
  if(ctx.isPointInPath(m.x, m.y)){
    ctx.stroke()
  }
})


// a function to detect the mouse position
 function oMousePos(canvas, evt) {
  var ClientRect = canvas.getBoundingClientRect();
 return {
 x: Math.round(evt.clientX - ClientRect.left),
 y: Math.round(evt.clientY - ClientRect.top)
  }
 }
#canvasContainer {
    width: 100%;
    height: 100vh;
    background-color: gray;
  }
<div id="canvasContainer">
  <canvas id="editorCanvas"></canvas>
</div>
enxaneta
  • 31,608
  • 5
  • 29
  • 42
0

The following is based on Durga's excellent answer, though with a couple of corrections.

Firstly, the selection box now draws to the canvas's regular context using getContext() rather than the contextTop. Since contextTop is used to draw the highlighted text selection box, clearing that context on mouseout meant text selection boxes would disappear when the mouse left the object.

Secondly, the selection box will now only render when hovering over an object that isn't already selected (this avoids a doubling effect where two overlapping boxes are drawn)

$(function() {
  var canvas = new fabric.Canvas('editorCanvas', {
    backgroundColor: 'white',
    selectionLineWidth: 2,
    width: $("#canvasContainer").width(),
    height: $("#canvasContainer").height()
  });

  var text = new fabric.IText('hello world', {
    left: 50,
    top: 50
  });
  canvas.add(text);
  canvas
  text.on('mouseover', function() {
    if (this === this.canvas.getActiveObject()) return;
    this._renderControls(this.canvas.getContext(), {
      hasControls: false
    });
  });
  
  text.on('mouseout', function() {
    this.canvas.requestRenderAll();
  });
});
<style>
  #canvasContainer {
    width: 100%;
    height: 100vh;
    background-color: gray;
  }
</style>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
  
<div id="canvasContainer">
  <canvas id="editorCanvas"></canvas>
</div>
melchiar
  • 2,782
  • 16
  • 27