Skip to content

Commit

Permalink
updated selection docs
Browse files Browse the repository at this point in the history
  • Loading branch information
asturur committed Sep 29, 2017
1 parent 512ffdd commit ad8059a
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 86 deletions.
14 changes: 10 additions & 4 deletions build/files/src/canvas.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -483,9 +483,10 @@
var ctx = this.contextCache,
originalColor = target.selectionBackgroundColor;

target.hasBorders = target.transparentCorners = false;
target.selectionBackgroundColor = '';

this.clearContext(ctx);

ctx.save();
ctx.transform.apply(ctx, this.viewportTransform);
target.render(ctx);
Expand All @@ -494,15 +495,15 @@
target === this._activeObject && target._renderControls(ctx, {
hasBorders: false,
transparentCorners: false
}, {
hasBorders: false,
});

target.selectionBackgroundColor = originalColor;

var isTransparent = fabric.util.isTransparent(
ctx, x, y, this.targetFindTolerance);

this.clearContext(ctx);

return isTransparent;
},

Expand Down Expand Up @@ -1421,15 +1422,20 @@
if (oldObjects.length > 0 && objects.length > 0) {
opt.selected = added;
opt.deselected = removed;
// added for backward compatibility
opt.updated = added[0] || removed[0];
opt.target = this._activeObject;
somethingChanged && this.fire('selection:updated', opt);
}
else if (objects.length > 0) {
// deprecated event
if (objects.length === 1) {
opt.target = added[0];
this.fire('object:selected', opt);
}
opt.target = undefined;
opt.selected = added;
// added for backward compatibility
opt.target = this._activeObject;
this.fire('selection:created', opt);
}
else if (oldObjects.length > 0) {
Expand Down
32 changes: 8 additions & 24 deletions build/files/src/mixins/object_geometry.mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,24 +252,6 @@
return xcount;
},

/**
* Returns width of an object's bounding rectangle
* @deprecated since 1.0.4
* @return {Number} width value
*/
getBoundingRectWidth: function() {
return this.getBoundingRect().width;
},

/**
* Returns height of an object's bounding rectangle
* @deprecated since 1.0.4
* @return {Number} height value
*/
getBoundingRectHeight: function() {
return this.getBoundingRect().height;
},

/**
* Returns coordinates of object's bounding rectangle (left, top, width, height)
* the box is intented as aligned to axis of canvas.
Expand Down Expand Up @@ -334,24 +316,26 @@
/**
* Scales an object to a given width, with respect to bounding box (scaling by x/y equally)
* @param {Number} value New width value
* @param {Boolean} absolute ignore viewport
* @return {fabric.Object} thisArg
* @chainable
*/
scaleToWidth: function(value) {
scaleToWidth: function(value, absolute) {
// adjust to bounding rect factor so that rotated shapes would fit as well
var boundingRectFactor = this.getBoundingRect().width / this.getScaledWidth();
var boundingRectFactor = this.getBoundingRect(absolute).width / this.getScaledWidth();
return this.scale(value / this.width / boundingRectFactor);
},

/**
* Scales an object to a given height, with respect to bounding box (scaling by x/y equally)
* @param {Number} value New height value
* @param {Boolean} absolute ignore viewport
* @return {fabric.Object} thisArg
* @chainable
*/
scaleToHeight: function(value) {
scaleToHeight: function(value, absolute) {
// adjust to bounding rect factor so that rotated shapes would fit as well
var boundingRectFactor = this.getBoundingRect().height / this.getScaledHeight();
var boundingRectFactor = this.getBoundingRect(absolute).height / this.getScaledHeight();
return this.scale(value / this.height / boundingRectFactor);
},

Expand All @@ -365,8 +349,8 @@
vpt = this.getViewportTransform(),
dim = absolute ? this._getTransformedDimensions() : this._calculateCurrentDimensions(),
currentWidth = dim.x, currentHeight = dim.y,
sinTh = Math.sin(theta),
cosTh = Math.cos(theta),
sinTh = theta ? Math.sin(theta) : 0,
cosTh = theta ? Math.cos(theta) : 1,
_angle = currentWidth > 0 ? Math.atan(currentHeight / currentWidth) : 0,
_hypotenuse = (currentWidth / Math.cos(_angle)) / 2,
offsetX = Math.cos(_angle + theta) * _hypotenuse,
Expand Down
6 changes: 4 additions & 2 deletions build/files/src/shapes/object.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -679,8 +679,9 @@
*/
_updateCacheCanvas: function() {
if (this.noScaleCache && this.canvas && this.canvas._currentTransform) {
var action = this.canvas._currentTransform.action;
if (action.slice && action.slice(0, 5) === 'scale') {
var target = this.canvas._currentTransform.target,
action = this.canvas._currentTransform.action;
if (this === target && action.slice && action.slice(0, 5) === 'scale') {
return false;
}
}
Expand Down Expand Up @@ -986,6 +987,7 @@
this.drawCacheOnCanvas(ctx);
}
else {
this._cacheCanvas = null;
this.dirty = false;
this.drawObject(ctx);
if (this.objectCaching && this.statefullCache) {
Expand Down
4 changes: 2 additions & 2 deletions build/files/src/util/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@
var xPoints = [points[0].x, points[1].x, points[2].x, points[3].x],
minX = fabric.util.array.min(xPoints),
maxX = fabric.util.array.max(xPoints),
width = Math.abs(minX - maxX),
width = maxX - minX,
yPoints = [points[0].y, points[1].y, points[2].y, points[3].y],
minY = fabric.util.array.min(yPoints),
maxY = fabric.util.array.max(yPoints),
height = Math.abs(minY - maxY);
height = maxY - minY;

return {
left: minX,
Expand Down
56 changes: 24 additions & 32 deletions lib/fabric.js
Original file line number Diff line number Diff line change
Expand Up @@ -673,11 +673,11 @@ fabric.CommonMethods = {
var xPoints = [points[0].x, points[1].x, points[2].x, points[3].x],
minX = fabric.util.array.min(xPoints),
maxX = fabric.util.array.max(xPoints),
width = Math.abs(minX - maxX),
width = maxX - minX,
yPoints = [points[0].y, points[1].y, points[2].y, points[3].y],
minY = fabric.util.array.min(yPoints),
maxY = fabric.util.array.max(yPoints),
height = Math.abs(minY - maxY);
height = maxY - minY;

return {
left: minX,
Expand Down Expand Up @@ -9194,9 +9194,10 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
var ctx = this.contextCache,
originalColor = target.selectionBackgroundColor;

target.hasBorders = target.transparentCorners = false;
target.selectionBackgroundColor = '';

this.clearContext(ctx);

ctx.save();
ctx.transform.apply(ctx, this.viewportTransform);
target.render(ctx);
Expand All @@ -9205,15 +9206,15 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
target === this._activeObject && target._renderControls(ctx, {
hasBorders: false,
transparentCorners: false
}, {
hasBorders: false,
});

target.selectionBackgroundColor = originalColor;

var isTransparent = fabric.util.isTransparent(
ctx, x, y, this.targetFindTolerance);

this.clearContext(ctx);

return isTransparent;
},

Expand Down Expand Up @@ -10132,15 +10133,20 @@ fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fab
if (oldObjects.length > 0 && objects.length > 0) {
opt.selected = added;
opt.deselected = removed;
// added for backward compatibility
opt.updated = added[0] || removed[0];
opt.target = this._activeObject;
somethingChanged && this.fire('selection:updated', opt);
}
else if (objects.length > 0) {
// deprecated event
if (objects.length === 1) {
opt.target = added[0];
this.fire('object:selected', opt);
}
opt.target = undefined;
opt.selected = added;
// added for backward compatibility
opt.target = this._activeObject;
this.fire('selection:created', opt);
}
else if (oldObjects.length > 0) {
Expand Down Expand Up @@ -12424,8 +12430,9 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
*/
_updateCacheCanvas: function() {
if (this.noScaleCache && this.canvas && this.canvas._currentTransform) {
var action = this.canvas._currentTransform.action;
if (action.slice && action.slice(0, 5) === 'scale') {
var target = this.canvas._currentTransform.target,
action = this.canvas._currentTransform.action;
if (this === target && action.slice && action.slice(0, 5) === 'scale') {
return false;
}
}
Expand Down Expand Up @@ -12731,6 +12738,7 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
this.drawCacheOnCanvas(ctx);
}
else {
this._cacheCanvas = null;
this.dirty = false;
this.drawObject(ctx);
if (this.objectCaching && this.statefullCache) {
Expand Down Expand Up @@ -14009,24 +14017,6 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
return xcount;
},

/**
* Returns width of an object's bounding rectangle
* @deprecated since 1.0.4
* @return {Number} width value
*/
getBoundingRectWidth: function() {
return this.getBoundingRect().width;
},

/**
* Returns height of an object's bounding rectangle
* @deprecated since 1.0.4
* @return {Number} height value
*/
getBoundingRectHeight: function() {
return this.getBoundingRect().height;
},

/**
* Returns coordinates of object's bounding rectangle (left, top, width, height)
* the box is intented as aligned to axis of canvas.
Expand Down Expand Up @@ -14091,24 +14081,26 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
/**
* Scales an object to a given width, with respect to bounding box (scaling by x/y equally)
* @param {Number} value New width value
* @param {Boolean} absolute ignore viewport
* @return {fabric.Object} thisArg
* @chainable
*/
scaleToWidth: function(value) {
scaleToWidth: function(value, absolute) {
// adjust to bounding rect factor so that rotated shapes would fit as well
var boundingRectFactor = this.getBoundingRect().width / this.getScaledWidth();
var boundingRectFactor = this.getBoundingRect(absolute).width / this.getScaledWidth();
return this.scale(value / this.width / boundingRectFactor);
},

/**
* Scales an object to a given height, with respect to bounding box (scaling by x/y equally)
* @param {Number} value New height value
* @param {Boolean} absolute ignore viewport
* @return {fabric.Object} thisArg
* @chainable
*/
scaleToHeight: function(value) {
scaleToHeight: function(value, absolute) {
// adjust to bounding rect factor so that rotated shapes would fit as well
var boundingRectFactor = this.getBoundingRect().height / this.getScaledHeight();
var boundingRectFactor = this.getBoundingRect(absolute).height / this.getScaledHeight();
return this.scale(value / this.height / boundingRectFactor);
},

Expand All @@ -14122,8 +14114,8 @@ fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.Stati
vpt = this.getViewportTransform(),
dim = absolute ? this._getTransformedDimensions() : this._calculateCurrentDimensions(),
currentWidth = dim.x, currentHeight = dim.y,
sinTh = Math.sin(theta),
cosTh = Math.cos(theta),
sinTh = theta ? Math.sin(theta) : 0,
cosTh = theta ? Math.cos(theta) : 1,
_angle = currentWidth > 0 ? Math.atan(currentHeight / currentWidth) : 0,
_hypotenuse = (currentWidth / Math.cos(_angle)) / 2,
offsetX = Math.cos(_angle + theta) * _hypotenuse,
Expand Down
46 changes: 24 additions & 22 deletions posts/_posts/2017-08-17-v2-breaking-changes-2.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,13 @@ <h3>How to react to user selection</h3>
<pre>selected
deselected</pre>
Events fired by canvas are:
<pre>object:selected
<pre>selection:created
selection:updated
before:selection:cleared
selection:cleared</pre>
selection:cleared
// deprecated
object:selected
</pre>
For each object are available 2 callbacks
<pre>onDeselect
onSelect</pre>
Expand All @@ -96,25 +100,23 @@ <h3>How to react to user selection</h3>
which mutating the properties of objects like selectable, evented or others, is not comfortable or
anyway brings to weird code paths.
</p>
<p>Follows a description of the most common user selection flows, and the fabric code behind them.</p>
<h4>Selection</h4>
<p>An user click on an object, no other active objects on canvas<br/>
<code>setActiveObject()</code> gets called.<br />
setActiveObject calls the <code>onSelect</code> method of the target object, passing as first argument an object with
`e` property containing the reference to the event.<br />
At this point you may have or not have assigned to onSelect some custom logic to intervene on the selection process.<br />
If onSelect returns true, the selection process ends here and nothing gets selected.
If it returns false as default, the object is set as selected, a canvas level event gets fired <code>object:selected</code>, and a object level one fires
<code>selected</code>, both of them having access to the original mouse event.
<h4>Deselection</h4>
<p>An user click on a non selectable area of the canvas, having an object selected.<br/>
<code>discardActiveObject()</code> gets called.<br />
First a canvas level event fires <code>before:selection:cleared</code>, getting in the option argument both the actual activeObject and the event.<br />
Then discardActiveObject calls the <code>onDeselect</code> method of the target object, passing as first argument an object with
`e` property containing the reference to the event and eventually a reference to the next target that is replacing this, in this case undefined.<br />
At this point you may have or not have assigned to onDeselect some custom logic to intervene on the selection process.<br />
If onDeselect returns true, the deselection process ends here and nothing gets deselected, no additional events fired.
If it returns false as default, the active object is removed from the property _activeObject, a canvas level event gets fired as <code>selection:cleared</code>, and a object level one fires
<code>deselected</code>, both of them having access to the original mouse event.
<h4>Firing events</h4>
<p>Those events are meant to intercept user interaction with the selection of fabric.<br/>
You should expect the canvas to fire <code>selection:created</code> every time the user pass from a situation
of no selection to something selected. Each object involved in the seleciton process, one or more, by click or <body>
click and drag, will fire the <code>selected</code> event.<br />
When <code>selection:created</code> fires, it receives as first argument of the callback an object that contains <thead>
property 'selected', with an array of all the selected fabric instances.<br />
When an user remove or add an object from the multiselection or switch from single to multi selection, or simply move the selection
from the object A to the object B, the event <code>selection:udpated</code> fires. The event receives as first argument of the
callback an object that contains both 'selected' and 'deselected'
property contaning an array of the objects that entered or exited the selection status. Each object involved in the process fires its
own single event.<br />
Finally when an user destroy a multiple selection or deselect an object, the event <code>selection:cleared</code> fires, getting in <thead>
arguments an object with the 'deselected' property. <br />
The event <code>object:selected</code> is considered deprecated and will be removed in the next major version of fabricjs.
<h4>Callbacks on multiple selection</h4>
The callbacks onSelect and onDeselect are not available yet on the click and drag selection or on the shift click selection and will
be added as a feature as soon as branch 2.0 is ready.
</p>
</div>

0 comments on commit ad8059a

Please sign in to comment.