如何在fabricJS中通过鼠标选择覆盖对象?

时间:2016-08-23 13:24:03

标签: javascript canvas html5-canvas selection fabricjs

我试图开发一种方法来选择下面分层的对象和(完全)被其他对象覆盖的对象。一个想法是选择顶部对象,然后通过doubleclick向下遍历各个层。这就是我现在所得到的:



var canvas = new fabric.Canvas("c");

fabric.util.addListener(canvas.upperCanvasEl, "dblclick", function (e) {
  var _canvas = canvas;
  var _mouse = _canvas.getPointer(e);
  var _active = _canvas.getActiveObject();
    
  if (e.target) {
    var _targets = _canvas.getObjects().filter(function (_obj) {
      return _obj.containsPoint(_mouse);
    });
      
    //console.warn(_targets);
      
    for (var _i=0, _max=_targets.length; _i<_max; _i+=1) {
      //check if target is currently active
      if (_targets[_i] == _active) {
       	//then select the one on the layer below
       	_targets[_i-1] && _canvas.setActiveObject(_targets[_i-1]);
         break;
        }
      }
    }
});

canvas
  .add(new fabric.Rect({
    top: 25,
    left: 25,
    width: 100,
    height: 100,
    fill: "red"
  }))
  .add(new fabric.Rect({
    top: 50,
    left: 50,
    width: 100,
    height: 100,
    fill: "green"
  }))
  .add(new fabric.Rect({
    top: 75,
    left: 75,
    width: 100,
    height: 100,
    fill: "blue"
  }))
  .renderAll();
&#13;
canvas {
 border: 1px solid;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.min.js"></script>
<canvas id="c" width="300" height="200"></canvas>
&#13;
&#13;
&#13;

如您所见,尝试从red范围内选择blue矩形不起作用。我只能选择greenblue。我想在第一个doubleclick工作(green被选中)之后,再次点击只选择blue,以便以下双击只能再次获得green

有解决方法吗?还有其他想法吗?

2 个答案:

答案 0 :(得分:2)

过了一段时间,我终于能够自己解决这个问题了。单击对象会将其置于顶部。在双击时,我尝试将对象放在当前对象后面一层。在另一个dblclick上,我得到了一个,等等。对我来说很有用,也可以选择完全覆盖的物体,而无需移动其他物体。

&#13;
&#13;
var canvas = new fabric.Canvas("c");

canvas.on("object:selected", function (e) {
  if (e.target) {
    e.target.bringToFront();
    this.renderAll();
  }
});

var _prevActive = 0;
var _layer = 0;

//
fabric.util.addListener(canvas.upperCanvasEl, "dblclick", function (e) {
    var _canvas = canvas;
    //current mouse position
    var _mouse = _canvas.getPointer(e);
    //active object (that has been selected on click)
    var _active = _canvas.getActiveObject();
    //possible dblclick targets (objects that share mousepointer)
    var _targets = _canvas.getObjects().filter(function (_obj) {
        return _obj.containsPoint(_mouse) && !_canvas.isTargetTransparent(_obj, _mouse.x, _mouse.y);
    });
    
    _canvas.deactivateAll();
      
    //new top layer target
    if (_prevActive !== _active) {
        //try to go one layer below current target
        _layer = Math.max(_targets.length-2, 0);
    }
    //top layer target is same as before
    else {
        //try to go one more layer down
        _layer = --_layer < 0 ? Math.max(_targets.length-2, 0) : _layer;
    }

    //get obj on current layer
    var _obj = _targets[_layer];

    if (_obj) {
    	_prevActive = _obj;
    	_obj.bringToFront();
    	_canvas.setActiveObject(_obj).renderAll();
    }
});

//create something to play with
canvas
  //fully covered rect is selectable with dblclicks
  .add(new fabric.Rect({
    top: 75,
    left: 75,
    width: 50,
    height: 50,
    fill: "black",
    stroke: "black",
    globalCompositeOperation: "xor",
    perPixelTargetFind: true
  }))
  .add(new fabric.Circle({
    top: 25,
    left: 25,
    radius: 50,
    fill: "rgba(255,0,0,.5)",
    stroke: "black",
    perPixelTargetFind: true
  }))
  .add(new fabric.Circle({
    top: 50,
    left: 50,
    radius: 50,
    fill: "rgba(0,255,0,.5)",
    stroke: "black",
    perPixelTargetFind: true
  }))
  .add(new fabric.Circle({
    top: 75,
    left: 75,
    radius: 50,
    fill: "rgba(0,0,255,.5)",
    stroke: "black",
    perPixelTargetFind: true
  }))
  .renderAll();
&#13;
canvas {
 border: 1px solid;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<canvas id="c" width="300" height="200"></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

我的任务有点不同 - 任务是挑选当前对象后面重叠的对象:

  1. 在所选对象上[左键单击]+[Cmd-Key] 以选择紧随其后的第一个重叠对象。
  2. 如果没有找到重叠的对象,则从顶层重新开始搜索

这个想法是,对于每个点击事件,拦截选定的对象并将其替换为我们想要的对象。

Fabric mouse-down 事件是这样的:

  1. 用户点击画布
  2. 在画布上:通过鼠标光标的坐标找到target-obj并将其存储在实例变量(canvas._target)中
  3. mouse:down:before 运行事件处理程序
  4. 将步骤(2)中找到的 target-obj 与当前选定的对象进行比较,根据比较结果触发 selection:cleared/update/create 事件。
  5. 设置新的活动对象
  6. mouse:down 运行事件处理程序

我们可以在 mouse:down:before 上使用自定义的事件处理程序来拦截在 Step(2) 中找到的 target-obj 并将其替换为我们想要的对象

fCanvas = new fabric.Canvas('my-canvas', {
  backgroundColor: '#cbf1f1',
  width: 800,
  height: 600,
  preserveObjectStacking: true
})

const r1 = new fabric.Rect({ width: 200, height: 200, stroke: null, top: 0, left: 0, fill:'red'})
const r2 = new fabric.Rect({ width: 200, height: 200, stroke: null, top: 50, left: 50, fill:'green'})
const r3 = new fabric.Rect({ width: 200, height: 200, stroke: null, top: 100, left: 100, fill:'yellow'})

fCanvas.add(r1, r2, r3)
fCanvas.requestRenderAll()

fCanvas.on('mouse:down:before', ev => {
  if (!ev.e.metaKey) {
    return
  }
  // Prevent conflicts with multi-selection
  if (ev.e[fCanvas.altSelectionKey]) {
    return
  }
  const currActiveObj = fCanvas.getActiveObject()
  if (!currActiveObj) {
    return
  }

  const pointer = fCanvas.getPointer(ev, true)
  const hitObj = fCanvas._searchPossibleTargets([currActiveObj], pointer)
  if (!hitObj) {
    return
  }

  let excludeObjs = []
  if (currActiveObj instanceof fabric.Group) {
    currActiveObj._objects.forEach(x => { excludeObjs.push(x) })
  } else {
    // Target is single active object
    excludeObjs.push(currActiveObj)
  }

  let remain = excludeObjs.length
  let objsToSearch = []
  let lastIdx = -1
  const canvasObjs = fCanvas._objects

  for (let i = canvasObjs.length-1; i >=0 ; i--) {
    if (remain === 0) {
      lastIdx = i
      break
    }
    const obj = canvasObjs[i]
    if (excludeObjs.includes(obj)) {
      remain -= 1
    } else {
      objsToSearch.push(obj)
    }
  }

  const headObjs = canvasObjs.slice(0, lastIdx+1)
  objsToSearch = objsToSearch.reverse().concat(headObjs)
  const found = fCanvas._searchPossibleTargets(objsToSearch, pointer)
  if (found) {
    fCanvas._target = found
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.1/fabric.min.js"></script>
<html>
<h4>Left-click + Cmd-key on overlapping area to pick the obj which is behind current one</h4>
<canvas id="my-canvas"></canvas>

</html>