问题
In Fabric.js an object can be moved in the canvas by left-clicking, holding and moving the mouse. In this condition, the selected object follows the mouse's cursor and object:moving
gets fired. Releasing the left mouse button exits this mode.
Is there a way to change this behavior and have an object enter this mode with different events? For example, click once to have the object follow the cursor (and have the object:moving
fire correctly) and then click a second time to release the object in it's final position?
I'm hoping to avoid writing this from scratch listening to the "mouse move" event, given that this is already implemented, but just the triggers to enter/exist this mode have to be changed.
回答1:
I implemented this by having a variable called movedObject
which is null
if no object is moved and contains an object if an object is moved. If you click while an object is being moved then it'll set movedObject
to null
. If you click while not moving an object then it'll set movedObject
to an object if you clicked one.
I added a listener to mouse:move
which sets left
and top
of movedObject
to the mouse x
and y
(minus movedObject
width
and height
/ 2) if movedObject
is not null
.
JSFiddle
Code:
var canvas = new fabric.Canvas('canvas');
canvas.setWidth(300);
canvas.setHeight(300);
var circle = new fabric.Circle({
radius: 30,
fill: '#f55',
top: 100,
left: 100
});
canvas.add(circle);
circle.hasControls = circle.hasBorders = circle.selectable = false;
var movedObject = null;
canvas.on({
'mouse:down': function(e) {
//If an object is selected already it'll be unselected
if (movedObject !== null) {
movedObject = null;
} else {
//If no object was selected and a click occured on an object it'll now be selected
if (e.target) {
movedObject = e.target;
}
}
},
'mouse:move': function(e) {
//Moving the object along with mouse cursor
if (movedObject !== null) {
movedObject.left = (e.e.clientX - movedObject.width / 2);
movedObject.top = (e.e.clientY - movedObject.height / 2);
movedObject.setCoords();
canvas.renderAll();
}
}
});
回答2:
Going through the Fabric.js code, the place where an object is marked to be moved is in canvas.__on_mouse_down(e)
which calls canvas._setupCurrentTransform(e, target)
(Only place where this method is used). This constructs an object and assigns it to canvas._currentTransform
. This object, in turn, is used in canvas.__onMouseMove(e)
to actually move the selected object using canvas._transformObject(e)
.
The problem is that canvas.__onMouseUp(e)
calls canvas._finalizeCurrentTransform()
, so the dragging stops there. Fortunately it also fires "mouse:up" right after. So we can override this in the callback:
var mydragging = false;
canvas.on('mouse:up', function(o) {
if (mydragging) {
mydragging = false;
canvas.currentTransform = null;
}
else {
canvas._setupCurrentTransform(o.e, o.target);
mydragging = true;
}
});
This puts the canvas
in the exact same state as when dragging by holding the mouse button down (before introducing this code), therefore all the built-in behavior remains in place, including firing "object:moving".
Unfortunately canvas._currentTransform
and canvas._setupCurrentTransform
are marked as private and cannot be relied upon. Perhaps they are willing to make it public.
来源:https://stackoverflow.com/questions/34981369/fabric-js-move-object-without-holding-mouse-button