我试图开发一种方法来选择下面分层的对象和(完全)被其他对象覆盖的对象。一个想法是选择顶部对象,然后通过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;
如您所见,尝试从red
范围内选择blue
矩形不起作用。我只能选择green
或blue
。我想在第一个doubleclick
工作(green
被选中)之后,再次点击只选择blue
,以便以下双击只能再次获得green
。
有解决方法吗?还有其他想法吗?
答案 0 :(得分:2)
过了一段时间,我终于能够自己解决这个问题了。单击对象会将其置于顶部。在双击时,我尝试将对象放在当前对象后面一层。在另一个dblclick上,我得到了一个,等等。对我来说很有用,也可以选择完全覆盖的物体,而无需移动其他物体。
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;
答案 1 :(得分:1)
我的任务有点不同 - 任务是挑选当前对象后面重叠的对象:
这个想法是,对于每个点击事件,拦截选定的对象并将其替换为我们想要的对象。
Fabric mouse-down
事件是这样的:
target-obj
并将其存储在实例变量(canvas._target)中mouse:down:before
运行事件处理程序target-obj
与当前选定的对象进行比较,根据比较结果触发 selection:cleared/update/create
事件。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>