Get pointer change (#3144)

* fix normalized pointer
* more tests
This commit is contained in:
Andrea Bogazzi 2016-08-07 17:00:58 +02:00 committed by GitHub
parent 03d27681c3
commit ad7e56422a
5 changed files with 550 additions and 234 deletions

View file

@ -225,6 +225,13 @@
*/
isDrawingMode: false,
/**
* Indicates whether objects should remain in current stack position when selected. When false objects are brought to top and rendered as part of the selection group
* @type Boolean
* @default
*/
preserveObjectStacking: false,
/**
* @private
*/
@ -242,6 +249,73 @@
this.calcOffset();
},
/**
* Divides objects in two groups, one to render immediately
* and one to render as activeGroup.
* @return {Array} objects to render immediately and pushes the other in the activeGroup.
*/
_chooseObjectsToRender: function() {
var activeGroup = this.getActiveGroup(),
activeObject = this.getActiveObject(),
object, objsToRender = [ ], activeGroupObjects = [ ];
if ((activeGroup || activeObject) && !this.preserveObjectStacking) {
for (var i = 0, length = this._objects.length; i < length; i++) {
object = this._objects[i];
if ((!activeGroup || !activeGroup.contains(object)) && object !== activeObject) {
objsToRender.push(object);
}
else {
activeGroupObjects.push(object);
}
}
if (activeGroup) {
activeGroup._set('_objects', activeGroupObjects);
objsToRender.push(activeGroup);
}
activeObject && objsToRender.push(activeObject);
}
else {
objsToRender = this._objects;
}
return objsToRender;
},
/**
* Renders both the top canvas and the secondary container canvas.
* @param {Boolean} [allOnTop] Whether we want to force all images to be rendered on the top canvas
* @return {fabric.Canvas} instance
* @chainable
*/
renderAll: function () {
if (this.selection && !this._groupSelector && !this.isDrawingMode) {
this.clearContext(this.contextTop);
}
var canvasToDrawOn = this.contextContainer;
this.renderCanvas(canvasToDrawOn, this._chooseObjectsToRender());
return this;
},
/**
* Method to render only the top canvas.
* Also used to render the group selection box.
* @return {fabric.Canvas} thisArg
* @chainable
*/
renderTop: function () {
var ctx = this.contextTop;
this.clearContext(ctx);
// we render the top context - last object
if (this.selection && this._groupSelector) {
this._drawSelection(ctx);
}
this.fire('after:render');
return this;
},
/**
* Resets the current transform to its original values and chooses the type of resizing based on the event
* @private
@ -299,8 +373,9 @@
* @return {Boolean} true if point is contained within an area of given object
*/
containsPoint: function (e, target, point) {
var pointer = point || this.getPointer(e, true),
xy = this._normalizePointer(target, pointer);
var ignoreZoom = true,
pointer = point || this.getPointer(e, ignoreZoom),
xy;
if (target.group && target.group === this.getActiveGroup()) {
xy = this._normalizePointer(target.group, pointer);
@ -317,18 +392,13 @@
* @private
*/
_normalizePointer: function (object, pointer) {
var lt, m;
m = fabric.util.multiplyTransformMatrices(
this.viewportTransform,
object.calcTransformMatrix());
m = fabric.util.invertTransform(m);
pointer = fabric.util.transformPoint(pointer, m , false);
lt = fabric.util.transformPoint(object.getCenterPoint(), m , false);
pointer.x -= lt.x;
pointer.y -= lt.y;
return { x: pointer.x, y: pointer.y };
var m = object.calcTransformMatrix(),
invertedM = fabric.util.invertTransform(m),
vpt = this.viewportTransform,
vptPointer = this.restorePointerVpt(pointer),
p = fabric.util.transformPoint(vptPointer, invertedM);
return fabric.util.transformPoint(p, vpt);
//return { x: p.x * vpt[0], y: p.y * vpt[3] };
},
/**
@ -864,6 +934,7 @@
},
/**
* @param {fabric.Object} target to reset transform
* @private
*/
_resetObjectTransform: function (target) {
@ -876,10 +947,10 @@
/**
* @private
* @param {CanvasRenderingContext2D} ctx to draw the selection on
*/
_drawSelection: function () {
var ctx = this.contextTop,
groupSelector = this._groupSelector,
_drawSelection: function (ctx) {
var groupSelector = this._groupSelector,
left = groupSelector.left,
top = groupSelector.top,
aleft = abs(left),
@ -933,9 +1004,10 @@
return;
}
var pointer = this.getPointer(e, true),
activeGroup = this.getActiveGroup(),
activeObject = this.getActiveObject();
var ignoreZoom = true,
pointer = this.getPointer(e, ignoreZoom),
activeGroup = this.getActiveGroup(),
activeObject = this.getActiveObject();
// first check current group (if one exists)
// active group does not check sub targets like normal groups.
@ -1020,6 +1092,18 @@
return target;
},
/**
* Returns pointer coordinates without the effect of the viewport
* @param {Object} pointer with "x" and "y" number values
* @return {Object} object with "x" and "y" number values
*/
restorePointerVpt: function(pointer) {
return fabric.util.transformPoint(
pointer,
fabric.util.invertTransform(this.viewportTransform)
);
},
/**
* Returns pointer coordinates relative to canvas.
* @param {Event} e
@ -1049,10 +1133,7 @@
pointer.x = pointer.x - this._offset.left;
pointer.y = pointer.y - this._offset.top;
if (!ignoreZoom) {
pointer = fabric.util.transformPoint(
pointer,
fabric.util.invertTransform(this.viewportTransform)
);
pointer = this.restorePointerVpt(pointer);
}
if (boundsWidth === 0 || boundsHeight === 0) {
@ -1195,6 +1276,20 @@
return this._activeObject;
},
/**
* @private
* @param {fabric.Object} obj Object that was removed
*/
_onObjectRemoved: function(obj) {
// removing active object should fire "selection:cleared" events
if (this.getActiveObject() === obj) {
this.fire('before:selection:cleared', { target: obj });
this._discardActiveObject();
this.fire('selection:cleared');
}
this.callSuper('_onObjectRemoved', obj);
},
/**
* @private
*/
@ -1323,6 +1418,18 @@
return this;
},
/**
* Clears all contexts (background, main, top) of an instance
* @return {fabric.Canvas} thisArg
* @chainable
*/
clear: function () {
this.discardActiveGroup();
this.discardActiveObject();
this.clearContext(this.contextTop);
return this.callSuper('clear');
},
/**
* Draws objects' controls (borders/controls)
* @param {CanvasRenderingContext2D} ctx Context to render controls on
@ -1350,22 +1457,6 @@
}
},
/**
* @private
* @param {fabric.Object} obj Object that was removed
*/
_onObjectRemoved: function(obj) {
this.callSuper('_onObjectRemoved', obj);
},
/**
* Clears all contexts (background, main, top) of an instance
* @return {fabric.Canvas} thisArg
* @chainable
*/
clear: function () {
return this.callSuper('clear');
}
});
// copying static properties manually to work around Opera's bug,

View file

@ -383,8 +383,7 @@
if (this.clipTo) {
fabric.util.clipContext(this, this.contextTop);
}
var ivt = fabric.util.invertTransform(this.viewportTransform),
pointer = fabric.util.transformPoint(this.getPointer(e, true), ivt);
var pointer = this.getPointer(e);
this.freeDrawingBrush.onMouseDown(pointer);
this._handleEvent(e, 'down');
},
@ -395,8 +394,7 @@
*/
_onMouseMoveInDrawingMode: function(e) {
if (this._isCurrentlyDrawing) {
var ivt = fabric.util.invertTransform(this.viewportTransform),
pointer = fabric.util.transformPoint(this.getPointer(e, true), ivt);
var pointer = this.getPointer(e);
this.freeDrawingBrush.onMouseMove(pointer);
}
this.setCursor(this.freeDrawingCursor);
@ -672,11 +670,13 @@
/**
* @private
* @param {Event} e Event object
* @param {Object} transform current tranform
* @param {Number} x mouse position x from origin
* @param {Number} y mouse poistion y from origin
* @return {Boolean} true if the scaling occurred
*/
_onScale: function(e, transform, x, y) {
// rotate object only if shift key is not pressed
// and if it is not a group we are transforming
if ((e[this.uniScaleKey] || this.uniScaleTransform) && !transform.target.get('lockUniScaling')) {
transform.currentAction = 'scale';
return this._scaleObject(x, y);

View file

@ -133,13 +133,6 @@
*/
imageSmoothingEnabled: true,
/**
* Indicates whether objects should remain in current stack position when selected. When false objects are brought to top and rendered as part of the selection group
* @type Boolean
* @default
*/
preserveObjectStacking: false,
/**
* The transformation (in the format of Canvas transform) which focuses the viewport
* @type Array
@ -181,6 +174,7 @@
* @param {Object} [options] Options object
*/
_initStatic: function(el, options) {
var cb = fabric.StaticCanvas.prototype.renderAll.bind(this);
this._objects = [];
this._createLowerCanvas(el);
@ -193,16 +187,16 @@
}
if (options.overlayImage) {
this.setOverlayImage(options.overlayImage, this.renderAll.bind(this));
this.setOverlayImage(options.overlayImage, cb);
}
if (options.backgroundImage) {
this.setBackgroundImage(options.backgroundImage, this.renderAll.bind(this));
this.setBackgroundImage(options.backgroundImage, cb);
}
if (options.backgroundColor) {
this.setBackgroundColor(options.backgroundColor, this.renderAll.bind(this));
this.setBackgroundColor(options.backgroundColor, cb);
}
if (options.overlayColor) {
this.setOverlayColor(options.overlayColor, this.renderAll.bind(this));
this.setOverlayColor(options.overlayColor, cb);
}
this.calcOffset();
},
@ -669,13 +663,13 @@
setViewportTransform: function (vpt) {
var activeGroup = this.getActiveGroup();
this.viewportTransform = vpt;
this.renderAll();
for (var i = 0, len = this._objects.length; i < len; i++) {
this._objects[i].setCoords();
}
if (activeGroup) {
activeGroup.setCoords();
}
this.renderAll();
return this;
},
@ -688,18 +682,14 @@
*/
zoomToPoint: function (point, value) {
// TODO: just change the scale, preserve other transformations
var before = point;
var before = point, vpt = this.viewportTransform.slice(0);
point = fabric.util.transformPoint(point, fabric.util.invertTransform(this.viewportTransform));
this.viewportTransform[0] = value;
this.viewportTransform[3] = value;
var after = fabric.util.transformPoint(point, this.viewportTransform);
this.viewportTransform[4] += before.x - after.x;
this.viewportTransform[5] += before.y - after.y;
this.renderAll();
for (var i = 0, len = this._objects.length; i < len; i++) {
this._objects[i].setCoords();
}
return this;
vpt[0] = value;
vpt[3] = value;
var after = fabric.util.transformPoint(point, vpt);
vpt[4] += before.x - after.x;
vpt[5] += before.y - after.y;
return this.setViewportTransform(vpt);
},
/**
@ -720,13 +710,10 @@
* @chainable true
*/
absolutePan: function (point) {
this.viewportTransform[4] = -point.x;
this.viewportTransform[5] = -point.y;
this.renderAll();
for (var i = 0, len = this._objects.length; i < len; i++) {
this._objects[i].setCoords();
}
return this;
var vpt = this.viewportTransform.slice(0);
vpt[4] = -point.x;
vpt[5] = -point.y;
return this.setViewportTransform(vpt);
},
/**
@ -783,13 +770,6 @@
* @param {fabric.Object} obj Object that was removed
*/
_onObjectRemoved: function(obj) {
// removing active object should fire "selection:cleared" events
if (this.getActiveObject() === obj) {
this.fire('before:selection:cleared', { target: obj });
this._discardActiveObject();
this.fire('selection:cleared');
}
this.fire('object:removed', { target: obj });
obj.fire('removed');
},
@ -820,97 +800,64 @@
*/
clear: function () {
this._objects.length = 0;
if (this.discardActiveGroup) {
this.discardActiveGroup();
}
if (this.discardActiveObject) {
this.discardActiveObject();
}
this.clearContext(this.contextContainer);
if (this.contextTop) {
this.clearContext(this.contextTop);
}
this.fire('canvas:cleared');
this.renderAll();
return this;
},
/**
* Divides objects in two groups, one to render immediately
* and one to render as activeGroup.
* return objects to render immediately and pushes the other in the activeGroup.
*/
_chooseObjectsToRender: function() {
var activeGroup = this.getActiveGroup(),
activeObject = this.getActiveObject(),
object, objsToRender = [ ], activeGroupObjects = [ ];
if ((activeGroup || activeObject) && !this.preserveObjectStacking) {
for (var i = 0, length = this._objects.length; i < length; i++) {
object = this._objects[i];
if ((!activeGroup || !activeGroup.contains(object)) && object !== activeObject) {
objsToRender.push(object);
}
else {
activeGroupObjects.push(object);
}
}
activeGroup && activeGroup._set('_objects', activeGroupObjects);
}
else {
objsToRender = this._objects;
}
return objsToRender;
},
/**
* Renders both the top canvas and the secondary container canvas.
* @param {Boolean} [allOnTop] Whether we want to force all images to be rendered on the top canvas
* Renders both the canvas.
* @return {fabric.Canvas} instance
* @chainable
*/
renderAll: function () {
var canvasToDrawOn = this.contextContainer, objsToRender;
if (this.contextTop && this.selection && !this._groupSelector && !this.isDrawingMode) {
this.clearContext(this.contextTop);
}
this.clearContext(canvasToDrawOn);
this.fire('before:render');
if (this.clipTo) {
fabric.util.clipContext(this, canvasToDrawOn);
}
this._renderBackground(canvasToDrawOn);
canvasToDrawOn.save();
objsToRender = this._chooseObjectsToRender();
//apply viewport transform once for all rendering process
canvasToDrawOn.transform.apply(canvasToDrawOn, this.viewportTransform);
this._renderObjects(canvasToDrawOn, objsToRender);
if (!this.preserveObjectStacking) {
objsToRender = [this.getActiveGroup(), this.getActiveObject()];
this._renderObjects(canvasToDrawOn, objsToRender);
}
canvasToDrawOn.restore();
if (!this.controlsAboveOverlay && this.interactive) {
this.drawControls(canvasToDrawOn);
}
if (this.clipTo) {
canvasToDrawOn.restore();
}
this._renderOverlay(canvasToDrawOn);
if (this.controlsAboveOverlay && this.interactive) {
this.drawControls(canvasToDrawOn);
}
this.fire('after:render');
var canvasToDrawOn = this.contextContainer;
this.renderCanvas(canvasToDrawOn, this._objects);
return this;
},
/**
* Renders background, objects, overlay and controls.
* @param {CanvasRenderingContext2D} ctx
* @param {Array} objects to render
* @return {fabric.Canvas} instance
* @chainable
*/
renderCanvas: function(ctx, objects) {
this.clearContext(ctx);
this.fire('before:render');
if (this.clipTo) {
fabric.util.clipContext(this, ctx);
}
this._renderBackground(ctx);
ctx.save();
//apply viewport transform once for all rendering process
ctx.transform.apply(ctx, this.viewportTransform);
this._renderObjects(ctx, objects);
ctx.restore();
if (!this.controlsAboveOverlay && this.interactive) {
this.drawControls(ctx);
}
if (this.clipTo) {
ctx.restore();
}
this._renderOverlay(ctx);
if (this.controlsAboveOverlay && this.interactive) {
this.drawControls(ctx);
}
this.fire('after:render');
},
/**
* dummy function for organization purpouse.
* @private
*/
drawControls: function() {
// NOOP
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
@ -967,26 +914,6 @@
this._renderBackgroundOrOverlay(ctx, 'overlay');
},
/**
* Method to render only the top canvas.
* Also used to render the group selection box.
* @return {fabric.Canvas} thisArg
* @chainable
*/
renderTop: function () {
var ctx = this.contextTop || this.contextContainer;
this.clearContext(ctx);
// we render the top context - last object
if (this.selection && this._groupSelector) {
this._drawSelection();
}
this.fire('after:render');
return this;
},
/**
* Returns coordinates of a center of canvas.
* Returned value is an object with top and left properties

View file

@ -48,6 +48,22 @@
'"shadow":null,'+
'"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0}],"background":"#ff5555","overlay":"rgba(0,0,0,0.2)"}';
function _createImageElement() {
return fabric.isLikelyNode ? new (require('canvas').Image)() : fabric.document.createElement('img');
}
function getAbsolutePath(path) {
var isAbsolute = /^https?:/.test(path);
if (isAbsolute) return path;
var imgEl = _createImageElement();
imgEl.src = path;
var src = imgEl.src;
imgEl = null;
return src;
}
var IMG_SRC = fabric.isLikelyNode ? (__dirname + '/../fixtures/test_image.gif') : getAbsolutePath('../fixtures/test_image.gif');
var el = fabric.document.createElement('canvas');
el.width = 600; el.height = 600;
@ -68,6 +84,8 @@
QUnit.module('fabric.Canvas', {
setup: function() {
upperCanvasEl.style.display = '';
canvas.controlsAboveOverlay = fabric.Canvas.prototype.controlsAboveOverlay;
canvas.preserveObjectStacking = fabric.Canvas.prototype.preserveObjectStacking;
},
teardown: function() {
canvas.clear();
@ -103,6 +121,83 @@
equal(canvas.item(0), rect, 'should return proper item');
});
test('preserveObjectStacking', function() {
ok(typeof canvas.preserveObjectStacking == 'boolean');
ok(!canvas.preserveObjectStacking, 'default is false');
});
test('uniScaleTransform', function() {
ok(typeof canvas.uniScaleTransform == 'boolean');
ok(!canvas.uniScaleTransform, 'default is false');
});
test('uniScaleKey', function() {
ok(typeof canvas.uniScaleKey == 'string');
equal(canvas.uniScaleKey, 'shiftKey', 'default is shift');
});
test('centeredScaling', function() {
ok(typeof canvas.centeredScaling == 'boolean');
ok(!canvas.centeredScaling, 'default is false');
});
test('centeredRotation', function() {
ok(typeof canvas.centeredRotation == 'boolean');
ok(!canvas.centeredRotation, 'default is false');
});
test('centeredKey', function() {
ok(typeof canvas.centeredKey == 'string');
equal(canvas.centeredKey, 'altKey', 'default is alt');
});
test('altActionKey', function() {
ok(typeof canvas.altActionKey == 'string');
equal(canvas.altActionKey, 'shiftKey', 'default is shift');
});
test('interactive', function() {
ok(typeof canvas.interactive == 'boolean');
ok(canvas.interactive, 'default is true');
});
test('selection', function() {
ok(typeof canvas.selection == 'boolean');
ok(canvas.selection, 'default is true');
});
test('_initInteractive', function() {
ok(typeof canvas._initInteractive == 'function');
});
test('renderTop', function() {
ok(typeof canvas.renderTop == 'function');
equal(canvas, canvas.renderTop());
});
test('_chooseObjectsToRender', function() {
ok(typeof canvas._chooseObjectsToRender == 'function');
var rect = makeRect(), rect2 = makeRect(), rect3 = makeRect();
canvas.add(rect);
canvas.add(rect2);
canvas.add(rect3);
var objs = canvas._chooseObjectsToRender();
equal(objs[0], rect);
equal(objs[1], rect2);
equal(objs[2], rect3);
canvas.setActiveObject(rect);
objs = canvas._chooseObjectsToRender();
equal(objs[0], rect2);
equal(objs[1], rect3);
equal(objs[2], rect);
canvas.setActiveObject(rect2);
canvas.preserveObjectStacking = true;
objs = canvas._chooseObjectsToRender();
equal(objs[0], rect);
equal(objs[1], rect2);
equal(objs[2], rect3);
});
test('calcOffset', function() {
ok(typeof canvas.calcOffset == 'function', 'should respond to `calcOffset`');
equal(canvas.calcOffset(), canvas, 'should be chainable');
@ -214,9 +309,8 @@
equal(canvas, canvas.renderAll());
});
test('renderTop', function() {
ok(typeof canvas.renderTop == 'function');
equal(canvas, canvas.renderTop());
test('_drawSelection', function() {
ok(typeof canvas._drawSelection == 'function');
});
test('findTarget', function() {
@ -793,6 +887,78 @@
});
});
test('loadFromJSON with custom properties on Canvas with no async object', function() {
var serialized = JSON.parse(PATH_JSON);
serialized.controlsAboveOverlay = true;
serialized.preserveObjectStacking = true;
equal(canvas.controlsAboveOverlay, fabric.Canvas.prototype.controlsAboveOverlay);
equal(canvas.preserveObjectStacking, fabric.Canvas.prototype.preserveObjectStacking);
canvas.loadFromJSON(serialized, function() {
ok(!canvas.isEmpty(), 'canvas is not empty');
equal(canvas.controlsAboveOverlay, true);
equal(canvas.preserveObjectStacking, true);
});
// if no async object the callback is called syncronously
equal(canvas.controlsAboveOverlay, true);
equal(canvas.preserveObjectStacking, true);
});
asyncTest('loadFromJSON with custom properties on Canvas with image', function() {
var JSON_STRING = '{"objects":[{"type":"image","originX":"left","originY":"top","left":13.6,"top":-1.4,"width":3000,"height":3351,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":0,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":0.05,"scaleY":0.05,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"src":"' + IMG_SRC + '","filters":[],"crossOrigin":"","alignX":"none","alignY":"none","meetOrSlice":"meet"}],'
+ '"background":"green"}';
var serialized = JSON.parse(JSON_STRING);
serialized.controlsAboveOverlay = true;
serialized.preserveObjectStacking = true;
equal(canvas.controlsAboveOverlay, fabric.Canvas.prototype.controlsAboveOverlay);
equal(canvas.preserveObjectStacking, fabric.Canvas.prototype.preserveObjectStacking);
canvas.loadFromJSON(serialized, function() {
ok(!canvas.isEmpty(), 'canvas is not empty');
equal(canvas.controlsAboveOverlay, true);
equal(canvas.preserveObjectStacking, true);
start();
});
// before callback the properties are still false.
equal(canvas.controlsAboveOverlay, false);
equal(canvas.preserveObjectStacking, false);
});
test('normalize pointer', function(){
ok(typeof canvas._normalizePointer == 'function');
var pointer = { x: 10, y: 20 },
object = makeRect({ top: 10, left: 10, width: 50, height: 50, strokeWidth: 0}),
normalizedPointer = canvas._normalizePointer(object, pointer);
equal(normalizedPointer.x, -25, 'should be in top left corner of rect');
equal(normalizedPointer.y, -15, 'should be in top left corner of rect');
object.angle = 90;
normalizedPointer = canvas._normalizePointer(object, pointer);
equal(normalizedPointer.x, -15, 'should consider angle');
equal(normalizedPointer.y, -25, 'should consider angle');
object.angle = 0;
object.scaleX = 2;
object.scaleY = 2;
normalizedPointer = canvas._normalizePointer(object, pointer);
equal(normalizedPointer.x, -25, 'should consider scale');
equal(normalizedPointer.y, -20, 'should consider scale');
object.skewX = 60;
normalizedPointer = canvas._normalizePointer(object, pointer);
equal(normalizedPointer.x.toFixed(2), -33.66, 'should consider skewX');
equal(normalizedPointer.y, -20, 'should not change');
});
test('restorePointerVpt', function(){
ok(typeof canvas.restorePointerVpt == 'function');
var pointer = { x: 10, y: 20 },
restoredPointer = canvas.restorePointerVpt(pointer);
equal(restoredPointer.x, pointer.x, 'no changes if not vpt is set');
equal(restoredPointer.y, pointer.y, 'no changes if not vpt is set');
canvas.viewportTransform = [2, 0, 0, 2, 50, -60];
restoredPointer = canvas.restorePointerVpt(pointer);
equal(restoredPointer.x, -20, 'vpt changes restored');
equal(restoredPointer.y, 40, 'vpt changes restored');
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
});
// asyncTest('loadFromJSON with backgroundImage', function() {
// canvas.setBackgroundImage('../../assets/pug.jpg');
// var anotherCanvas = new fabric.Canvas();
@ -1315,6 +1481,104 @@
ok(canvas.containsPoint(eventStub, rect), 'on rect at (200, 200) should be within area (175, 175, 225, 225)');
});
test('setupCurrentTransform', function() {
ok(typeof canvas._setupCurrentTransform == 'function');
var rect = new fabric.Rect({ left: 75, top: 75, width: 50, height: 50 });
canvas.add(rect);
var canvasEl = canvas.getElement(),
canvasOffset = fabric.util.getElementOffset(canvasEl);
var eventStub = {
clientX: canvasOffset.left + 100,
clientY: canvasOffset.top + 100,
target: rect
};
rect.active = true;
canvas._setupCurrentTransform(eventStub, rect);
var t = canvas._currentTransform;
equal(t.target, rect, 'should have rect as a target');
equal(t.action, 'drag', 'should target inside rect and setup drag');
equal(t.corner, 0, 'no corner selected');
equal(t.originX, rect.originX, 'no origin change for drag');
equal(t.originY, rect.originY, 'no origin change for drag');
eventStub = {
clientX: canvasOffset.left + rect.oCoords.tl.corner.tl.x + 1,
clientY: canvasOffset.top + rect.oCoords.tl.corner.tl.y + 1,
target: rect
};
canvas._setupCurrentTransform(eventStub, rect);
t = canvas._currentTransform;
equal(t.target, rect, 'should have rect as a target');
equal(t.action, 'scale', 'should target a corner and setup scale');
equal(t.corner, 'tl', 'tl selected');
equal(t.originX, 'right', 'origin in opposite direction');
equal(t.originY, 'bottom', 'origin in opposite direction');
equal(t.shiftKey, undefined, 'shift was not pressed');
eventStub = {
clientX: canvasOffset.left + rect.left - 2,
clientY: canvasOffset.top + rect.top + rect.height/2,
target: rect,
shiftKey: true
};
canvas._setupCurrentTransform(eventStub, rect);
t = canvas._currentTransform;
equal(t.target, rect, 'should have rect as a target');
equal(t.action, 'skewY', 'should target a corner and setup skew');
equal(t.shiftKey, true, 'shift was pressed');
equal(t.corner, 'ml', 'ml selected');
equal(t.originX, 'right', 'origin in opposite direction');
eventStub = {
clientX: canvasOffset.left + rect.oCoords.mtr.x,
clientY: canvasOffset.top + rect.oCoords.mtr.y,
target: rect,
};
canvas._setupCurrentTransform(eventStub, rect);
t = canvas._currentTransform;
equal(t.target, rect, 'should have rect as a target');
equal(t.action, 'rotate', 'should target a corner and setup rotate');
equal(t.corner, 'mtr', 'mtr selected');
canvas._currentTransform = false;
});
test('_scaleObject', function() {
ok(typeof canvas._scaleObject == 'function');
var rect = new fabric.Rect({ left: 75, top: 75, width: 50, height: 50 });
canvas.add(rect);
var canvasEl = canvas.getElement(),
canvasOffset = fabric.util.getElementOffset(canvasEl);
var eventStub = {
clientX: canvasOffset.left + rect.oCoords.tl.corner.tl.x + 1,
clientY: canvasOffset.top + rect.oCoords.tl.corner.tl.y + 1,
target: rect
};
canvas._setupCurrentTransform(eventStub, rect);
var scaled = canvas._scaleObject(30, 30, 'equally');
equal(scaled, true, 'return true if scaling happened');
scaled = canvas._scaleObject(30, 30, 'equally');
equal(scaled, false, 'return false if no movement happen');
});
test('containsPoint in viewport transform', function() {
canvas.viewportTransform = [2, 0, 0, 2, 50, 50];
var rect = new fabric.Rect({ left: 75, top: 75, width: 50, height: 50 });
canvas.add(rect);
var canvasEl = canvas.getElement(),
canvasOffset = fabric.util.getElementOffset(canvasEl);
var eventStub = {
clientX: canvasOffset.left + 250,
clientY: canvasOffset.top + 250,
target: rect
};
ok(canvas.containsPoint(eventStub, rect), 'point at (250, 250) should be within area (75, 75, 125, 125)');
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
});
asyncTest('fxRemove', function() {
ok(typeof canvas.fxRemove == 'function');

View file

@ -81,7 +81,7 @@
'backgroundColor': '',
'clipTo': null,
'filters': [],
'resizeFilters': [],
'resizeFilters': [],
'fillRule': 'nonzero',
'globalCompositeOperation': 'source-over',
'transformMatrix': null,
@ -158,9 +158,7 @@
canvas.backgroundColor = fabric.StaticCanvas.prototype.backgroundColor;
canvas.backgroundImage = fabric.StaticCanvas.prototype.backgroundImage;
canvas.overlayColor = fabric.StaticCanvas.prototype.overlayColor;
canvas.controlsAboveOverlay = fabric.StaticCanvas.prototype.controlsAboveOverlay;
canvas.preserveObjectStacking = fabric.StaticCanvas.prototype.preserveObjectStacking;
canvas.viewportTransform = fabric.StaticCanvas.prototype.viewportTransform;
canvas.viewportTransform = [1, 0, 0, 1, 0, 0];
canvas.calcOffset();
}
});
@ -434,16 +432,6 @@
equal(canvas, canvas.renderAll());
});
test('preserveObjectStacking', function() {
ok(typeof canvas.preserveObjectStacking == 'boolean');
ok(!canvas.preserveObjectStacking);
});
test('renderTop', function() {
ok(typeof canvas.renderTop == 'function');
equal(canvas, canvas.renderTop());
});
test('toDataURL', function() {
ok(typeof canvas.toDataURL == 'function');
if (!fabric.Canvas.supports('toDataURL')) {
@ -486,7 +474,7 @@
equal(rect.getCenterPoint().x, canvas.width / 2, 'object\'s "center.y" property should correspond to canvas element\'s center');
canvas.setZoom(4);
equal(rect.getCenterPoint().x, canvas.height / 2, 'object\'s "center.x" property should correspond to canvas element\'s center when canvas is transformed');
canvas.setZoom(1);
});
test('centerObjectV', function() {
@ -511,6 +499,7 @@
canvas.setZoom(4);
equal(rect.getCenterPoint().y, canvas.height / 2, 'object\'s "center.y" property should correspond to canvas element\'s center when canvas is transformed');
equal(rect.getCenterPoint().x, canvas.height / 2, 'object\'s "center.x" property should correspond to canvas element\'s center when canvas is transformed');
canvas.setZoom(1);
});
test('viewportCenterObjectH', function() {
@ -924,42 +913,6 @@
});
});
test('loadFromJSON with custom properties on Canvas with no async object', function() {
var serialized = JSON.parse(PATH_JSON);
serialized.controlsAboveOverlay = true;
serialized.preserveObjectStacking = true;
equal(canvas.controlsAboveOverlay, fabric.Canvas.prototype.controlsAboveOverlay);
equal(canvas.preserveObjectStacking, fabric.Canvas.prototype.preserveObjectStacking);
canvas.loadFromJSON(serialized, function() {
ok(!canvas.isEmpty(), 'canvas is not empty');
equal(canvas.controlsAboveOverlay, true);
equal(canvas.preserveObjectStacking, true);
});
// if no async object the callback is called syncronously
equal(canvas.controlsAboveOverlay, true);
equal(canvas.preserveObjectStacking, true);
});
asyncTest('loadFromJSON with custom properties on Canvas with image', function() {
var JSON_STRING = '{"objects":[{"type":"image","originX":"left","originY":"top","left":13.6,"top":-1.4,"width":3000,"height":3351,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":0,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":10,"scaleX":0.05,"scaleY":0.05,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"src":"' + IMG_SRC + '","filters":[],"crossOrigin":"","alignX":"none","alignY":"none","meetOrSlice":"meet"}],'
+ '"background":"green"}';
var serialized = JSON.parse(JSON_STRING);
serialized.controlsAboveOverlay = true;
serialized.preserveObjectStacking = true;
equal(canvas.controlsAboveOverlay, fabric.Canvas.prototype.controlsAboveOverlay);
equal(canvas.preserveObjectStacking, fabric.Canvas.prototype.preserveObjectStacking);
canvas.loadFromJSON(serialized, function() {
ok(!canvas.isEmpty(), 'canvas is not empty');
equal(canvas.controlsAboveOverlay, true);
equal(canvas.preserveObjectStacking, true);
start();
});
// before callback the properties are still false.
equal(canvas.controlsAboveOverlay, false);
equal(canvas.preserveObjectStacking, false);
});
asyncTest('loadFromJSON with image background and color', function() {
var serialized = JSON.parse(PATH_JSON);
serialized.background = 'green';
@ -1324,6 +1277,87 @@
});
});
test('setViewportTransform', function() {
ok(typeof canvas.setViewportTransform == 'function');
var vpt = [2, 0, 0, 2, 50, 50];
canvas.viewportTransform = fabric.StaticCanvas.prototype.viewportTransform;
deepEqual(canvas.viewportTransform, [1, 0, 0, 1, 0, 0], 'initial viewport is identity matrix');
canvas.setViewportTransform(vpt);
deepEqual(canvas.viewportTransform, vpt, 'viewport now is the set one');
canvas.viewportTransform = fabric.StaticCanvas.prototype.viewportTransform;
});
test('getZoom', function() {
ok(typeof canvas.getZoom == 'function');
var vpt = [2, 0, 0, 2, 50, 50];
canvas.viewportTransform = fabric.StaticCanvas.prototype.viewportTransform;
deepEqual(canvas.getZoom(), 1, 'initial zoom is 1');
canvas.setViewportTransform(vpt);
deepEqual(canvas.getZoom(), 2, 'zoom is set to 2');
canvas.viewportTransform = fabric.StaticCanvas.prototype.viewportTransform;
});
test('setZoom', function() {
ok(typeof canvas.setZoom == 'function');
deepEqual(canvas.getZoom(), 1, 'initial zoom is 1');
canvas.setZoom(2);
deepEqual(canvas.getZoom(), 2, 'zoom is set to 2');
canvas.viewportTransform = fabric.StaticCanvas.prototype.viewportTransform;
});
test('zoomToPoint', function() {
ok(typeof canvas.zoomToPoint == 'function');
deepEqual(canvas.viewportTransform, [1, 0, 0, 1, 0, 0], 'initial viewport is identity matrix');
var point = new fabric.Point(50, 50);
canvas.zoomToPoint(point, 1);
deepEqual(canvas.viewportTransform, [1, 0, 0, 1, 0, 0], 'viewport has no changes if not moving with zoom level');
canvas.zoomToPoint(point, 2);
deepEqual(canvas.viewportTransform, [2, 0, 0, 2, -50, -50], 'viewport has a translation effect and zoom');
canvas.zoomToPoint(point, 3);
deepEqual(canvas.viewportTransform, [3, 0, 0, 3, -100, -100], 'viewport has a translation effect and zoom');
canvas.viewportTransform = fabric.StaticCanvas.prototype.viewportTransform;
});
test('absolutePan', function() {
ok(typeof canvas.absolutePan == 'function');
deepEqual(canvas.viewportTransform, [1, 0, 0, 1, 0, 0], 'initial viewport is identity matrix');
var point = new fabric.Point(50, 50);
canvas.absolutePan(point);
deepEqual(canvas.viewportTransform, [1, 0, 0, 1, -point.x, -point.y], 'viewport has translation effect applied');
canvas.absolutePan(point);
deepEqual(canvas.viewportTransform, [1, 0, 0, 1, -point.x, -point.y], 'viewport has same translation effect applied');
canvas.viewportTransform = fabric.StaticCanvas.prototype.viewportTransform;
});
test('relativePan', function() {
ok(typeof canvas.relativePan == 'function');
deepEqual(canvas.viewportTransform, [1, 0, 0, 1, 0, 0], 'initial viewport is identity matrix');
var point = new fabric.Point(-50, -50);
canvas.relativePan(point);
deepEqual(canvas.viewportTransform, [1, 0, 0, 1, -50, -50], 'viewport has translation effect applied');
canvas.relativePan(point);
deepEqual(canvas.viewportTransform, [1, 0, 0, 1, -100, -100], 'viewport has translation effect applied on top of old one');
canvas.viewportTransform = fabric.StaticCanvas.prototype.viewportTransform;
});
test('getActiveObject', function() {
ok(typeof canvas.getActiveObject == 'function');
var activeObject = canvas.getActiveObject();
equal(activeObject, null, 'should return null');
});
test('getActiveGroup', function() {
ok(typeof canvas.getActiveGroup == 'function');
var activeGroup = canvas.getActiveGroup();
equal(activeGroup, null, 'should return null');
});
test('getContext', function() {
ok(typeof canvas.getContext == 'function');
var context = canvas.getContext();
equal(context, canvas.contextContainer, 'should return the context container');
});
//how to test with an exception?
/*asyncTest('options in setBackgroundImage from invalid URL', function() {
canvas.backgroundImage = null;