Reorganize fabric.Element in such way so that centering and aligning guidelines could work together.

`fabric.Element#onObjectMove` callback is gone, replaced by "object:moved" event (which allows subscription to the event by multiple parties).
Similarly, `fabric.Element#onMouseUp` is replaced with "mouse:up" event, and `fabric.Element#afterRender` with "after:render" one.
The drawback of these events, as of now, is that it's not possible to determine which canvas instance fired which event --
in case of multiple canvas instances in a document, this could get hairy. Will probably fix it by introducing some kind of `Observable` mixin,
which would add "observe" and "fire" methods to `fabric.Element` itself.
This commit is contained in:
kangax 2010-10-31 22:42:20 -04:00
parent 33467d15a2
commit 33278ae20b
5 changed files with 144 additions and 102 deletions

36
dist/all.js vendored
View file

@ -3574,10 +3574,6 @@ fabric.util.animate = animate;
this.setOverlayImage(options.overlayImage);
}
if (options.afterRender) {
this.afterRender = options.afterRender;
}
this._createCanvasBackground();
this._createCanvasContainer();
this._initEvents();
@ -3671,19 +3667,6 @@ fabric.util.animate = animate;
/* NOOP */
},
/**
* Callback; invoked every time active object is moved
* @method onObjectMove
* @param {fabric.Object} object that's being moved
*/
onObjectMove: null,
/**
* Callback; invoked when a mouseup event occurs, and at the end of all other transformations
* @method onMouseUp
*/
onMouseUp: null,
/**
* Calculates canvas element offset relative to the document
* This method is also attached as "resize" event handler of window
@ -4019,9 +4002,7 @@ fabric.util.animate = animate;
_this._setCursorFromEvent(e, target);
}, 50);
if (this.onMouseUp) {
this.onMouseUp();
}
fireEvent('mouse:up');
},
_shouldClearSelection: function (e) {
@ -4349,9 +4330,10 @@ fabric.util.animate = animate;
}
else {
this._translateObject(x, y);
if (this.onObjectMove) {
this.onObjectMove(this._currentTransform.target);
}
fireEvent('object:moved', {
target: this._currentTransform.target
});
}
this.renderAll();
}
@ -4659,9 +4641,7 @@ fabric.util.animate = animate;
var elapsedTime = new Date() - startTime;
this.onFpsUpdate(~~(1000 / elapsedTime));
if (this.afterRender) {
this.afterRender();
}
fireEvent('after:render');
return this;
},
@ -4689,9 +4669,7 @@ fabric.util.animate = animate;
activeGroup.render(this.contextTop);
}
if (this.afterRender) {
this.afterRender();
}
fireEvent('after:render');
return this;
},

View file

@ -126,10 +126,6 @@
this.setOverlayImage(options.overlayImage);
}
if (options.afterRender) {
this.afterRender = options.afterRender;
}
this._createCanvasBackground();
this._createCanvasContainer();
this._initEvents();
@ -223,19 +219,6 @@
/* NOOP */
},
/**
* Callback; invoked every time active object is moved
* @method onObjectMove
* @param {fabric.Object} object that's being moved
*/
onObjectMove: null,
/**
* Callback; invoked when a mouseup event occurs, and at the end of all other transformations
* @method onMouseUp
*/
onMouseUp: null,
/**
* Calculates canvas element offset relative to the document
* This method is also attached as "resize" event handler of window
@ -579,9 +562,7 @@
_this._setCursorFromEvent(e, target);
}, 50);
if (this.onMouseUp) {
this.onMouseUp();
}
fireEvent('mouse:up');
},
_shouldClearSelection: function (e) {
@ -935,9 +916,10 @@
}
else {
this._translateObject(x, y);
if (this.onObjectMove) {
this.onObjectMove(this._currentTransform.target);
}
fireEvent('object:moved', {
target: this._currentTransform.target
});
}
// only commit here. when we are actually moving the pictures
this.renderAll();
@ -1253,9 +1235,7 @@
var elapsedTime = new Date() - startTime;
this.onFpsUpdate(~~(1000 / elapsedTime));
if (this.afterRender) {
this.afterRender();
}
fireEvent('after:render');
return this;
},
@ -1286,9 +1266,7 @@
activeGroup.render(this.contextTop);
}
if (this.afterRender) {
this.afterRender();
}
fireEvent('after:render');
return this;
},

View file

@ -1,3 +1,8 @@
/**
* Should objects by aligned by a bounding box?
* [Bug] Scaled objects sometimes can not be aligned by edges
*
*/
function initAligningGuidelines(canvas) {
var ctx = canvas.getContext(),
@ -35,6 +40,8 @@ function initAligningGuidelines(canvas) {
}
function isInRange(value1, value2) {
value1 = Math.round(value1);
value2 = Math.round(value2);
for (var i = value1 - aligningLineMargin, len = value1 + aligningLineMargin; i <= len; i++) {
if (i === value2) {
return true;
@ -43,15 +50,21 @@ function initAligningGuidelines(canvas) {
return false;
}
canvas.onObjectMove = function(activeObject) {
var verticalLines = [ ],
horizontalLines = [ ];
fabric.util.observeEvent('object:moved', function(e) {
var canvasObjects = canvas.getObjects(),
var activeObject = e.memo.target,
canvasObjects = canvas.getObjects(),
activeObjectLeft = activeObject.get('left'),
activeObjectTop = activeObject.get('top'),
activeObjectHeight = activeObject.get('height'),
activeObjectWidth = activeObject.get('width'),
verticalLines = [ ],
horizontalLines = [ ];
activeObjectHeight = activeObject.getHeight(),
activeObjectWidth = activeObject.getWidth(),
noneInTheRange = true;
// It should be trivial to DRY this up by encapsulating (repeating) creation of x1, x2, y1, and y2 into functions,
// but we're not doing it here for perf. reasons -- as this a function that's invoked on every mouse move
for (var i = canvasObjects.length; i--; ) {
@ -62,8 +75,10 @@ function initAligningGuidelines(canvas) {
objectHeight = canvasObjects[i].getHeight(),
objectWidth = canvasObjects[i].getWidth();
// snap by the horizontal center line
if (isInRange(objectLeft, activeObjectLeft)) {
verticalLines.push({
noneInTheRange = false;
verticalLines.push({
x: objectLeft,
y1: (objectTop < activeObjectTop)
? (objectTop - objectHeight / 2 - aligningLineOffset)
@ -75,7 +90,39 @@ function initAligningGuidelines(canvas) {
activeObject.set('left', objectLeft);
}
// snap by the left edge
if (isInRange(objectLeft - objectWidth / 2, activeObjectLeft - activeObjectWidth / 2)) {
noneInTheRange = false;
verticalLines.push({
x: objectLeft - objectWidth / 2,
y1: (objectTop < activeObjectTop)
? (objectTop - objectHeight / 2 - aligningLineOffset)
: (objectTop + objectHeight / 2 + aligningLineOffset),
y2: (activeObjectTop > objectTop)
? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset)
: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset)
});
activeObject.set('left', objectLeft - objectWidth / 2 + activeObjectWidth / 2);
}
// snap by the right edge
if (isInRange(objectLeft + objectWidth / 2, activeObjectLeft + activeObjectWidth / 2)) {
noneInTheRange = false;
verticalLines.push({
x: objectLeft + objectWidth / 2,
y1: (objectTop < activeObjectTop)
? (objectTop - objectHeight / 2 - aligningLineOffset)
: (objectTop + objectHeight / 2 + aligningLineOffset),
y2: (activeObjectTop > objectTop)
? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset)
: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset)
});
activeObject.set('left', objectLeft + objectWidth / 2 - activeObjectWidth / 2);
}
// snap by the vertical center line
if (isInRange(objectTop, activeObjectTop)) {
noneInTheRange = false;
horizontalLines.push({
y: objectTop,
x1: (objectLeft < activeObjectLeft)
@ -87,17 +134,54 @@ function initAligningGuidelines(canvas) {
});
activeObject.set('top', objectTop);
}
// snap by the top edge
if (isInRange(objectTop - objectHeight / 2, activeObjectTop - activeObjectHeight / 2)) {
noneInTheRange = false;
horizontalLines.push({
y: objectTop - objectHeight / 2,
x1: (objectLeft < activeObjectLeft)
? (objectLeft - objectWidth / 2 - aligningLineOffset)
: (objectLeft + objectWidth / 2 + aligningLineOffset),
x2: (activeObjectLeft > objectLeft)
? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset)
: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset)
});
activeObject.set('top', objectTop - objectHeight / 2 + activeObjectHeight / 2);
}
// snap by the bottom edge
if (isInRange(objectTop + objectHeight / 2, activeObjectTop + activeObjectHeight / 2)) {
noneInTheRange = false;
horizontalLines.push({
y: objectTop + objectHeight / 2,
x1: (objectLeft < activeObjectLeft)
? (objectLeft - objectWidth / 2 - aligningLineOffset)
: (objectLeft + objectWidth / 2 + aligningLineOffset),
x2: (activeObjectLeft > objectLeft)
? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset)
: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset)
});
activeObject.set('top', objectTop + objectHeight / 2 - activeObjectHeight / 2);
}
}
canvas.afterRender = function() {
for (var i = verticalLines.length; i--; ) {
drawVerticalLine(verticalLines[i]);
}
for (var i = horizontalLines.length; i--; ) {
drawHorizontalLine(horizontalLines[i]);
}
};
};
if (noneInTheRange) {
verticalLines.length = horizontalLines.length = 0;
}
});
fabric.util.observeEvent('after:render', function() {
for (var i = verticalLines.length; i--; ) {
drawVerticalLine(verticalLines[i]);
}
for (var i = horizontalLines.length; i--; ) {
drawHorizontalLine(horizontalLines[i]);
}
});
fabric.util.observeEvent('mouse:up', function() {
verticalLines.length = horizontalLines.length = 0;
canvas.renderAll();
});
}

View file

@ -43,32 +43,37 @@ function initCenteringGuidelines(canvas) {
ctx.restore();
}
canvas.onObjectMove = function(object) {
var isInVerticalCenter = object.get('left') in canvasWidthCenterMap,
isInHorizontalCenter = object.get('top') in canvasHeightCenterMap;
var observeEvent = fabric.util.observeEvent,
afterRenderActions = [ ],
isInVerticalCenter,
isInHorizontalCenter;
observeEvent('object:moved', function(e) {
object = e.memo.target;
if (isInVerticalCenter || isInHorizontalCenter) {
canvas.afterRender = function() {
if (isInHorizontalCenter) {
showHorizontalCenterLine();
}
if (isInVerticalCenter) {
showVerticalCenterLine();
}
};
if (isInHorizontalCenter) {
object.set('top', canvasHeightCenter);
}
if (isInVerticalCenter) {
object.set('left', canvasWidthCenter);
}
isInVerticalCenter = object.get('left') in canvasWidthCenterMap,
isInHorizontalCenter = object.get('top') in canvasHeightCenterMap;
if (isInHorizontalCenter) {
object.set('top', canvasHeightCenter);
}
else {
canvas.afterRender = null;
if (isInVerticalCenter) {
object.set('left', canvasWidthCenter);
}
};
canvas.onMouseUp = function() {
canvas.afterRender = null;
});
observeEvent('after:render', function() {
if (isInVerticalCenter) {
showVerticalCenterLine();
}
if (isInHorizontalCenter) {
showHorizontalCenterLine();
}
});
observeEvent('mouse:up', function() {
// clear these values, to stop drawing guidelines once mouse is up
isInVerticalCenter = isInHorizontalCenter = null;
canvas.renderAll();
};
});
}

View file

@ -332,9 +332,6 @@
}, 100);
initCenteringGuidelines(canvas);
if (document.location.search.indexOf('align') > -1) {
initAligningGuidelines(canvas);
}
initAligningGuidelines(canvas);
})(this);