diff --git a/src/mixins/object_geometry.mixin.js b/src/mixins/object_geometry.mixin.js index d06a0ebd86e..152d8d7b5d8 100644 --- a/src/mixins/object_geometry.mixin.js +++ b/src/mixins/object_geometry.mixin.js @@ -164,6 +164,15 @@ return true; } } + // no points on screen, check intersection with absolute coordinates + if (this.intersectsWithRect(pointTL, pointBR, true)) { + return true; + } + // worst case scenario the object is so big that contanins the screen + var centerPoint = { x: (pointTL.x + pointBR.x) / 2, y: (pointTL.y + pointBR.y) / 2 }; + if (this.containsPoint(centerPoint, null, true)) { + return true; + } return false; }, diff --git a/src/shapes/object.class.js b/src/shapes/object.class.js index 4ce6c1d3ffc..f143b3b4f69 100644 --- a/src/shapes/object.class.js +++ b/src/shapes/object.class.js @@ -1134,6 +1134,9 @@ if ((this.width === 0 && this.height === 0) || !this.visible) { return; } + if (this.canvas && this.canvas.skipOffscreen && !this.group && !this.isOnScreen()) { + return; + } ctx.save(); //setup fill rule for current object this._setupCompositeOperation(ctx); diff --git a/src/shapes/text.class.js b/src/shapes/text.class.js index 33496e37039..dd4dd16e69b 100644 --- a/src/shapes/text.class.js +++ b/src/shapes/text.class.js @@ -849,6 +849,9 @@ if (!this.visible) { return; } + if (this.canvas && this.canvas.skipOffscreen && !this.group && !this.isOnScreen()) { + return; + } if (this._shouldClearDimensionCache()) { this._setTextStyles(ctx); this._initDimensions(ctx); diff --git a/src/static_canvas.class.js b/src/static_canvas.class.js index eadb8b53a6c..ac4b66af375 100644 --- a/src/static_canvas.class.js +++ b/src/static_canvas.class.js @@ -181,6 +181,16 @@ */ vptCoords: { }, + /** + * Based on vptCoords and object.aCoords, skip rendering of objects that + * are not included in current viewport. + * May greatly help in applications with crowded canvas and use of zoom/pan + * If One of the corner of the bounding box of the object is on the canvas + * the objects get rendered. + * @memberOf fabric.StaticCanvas.prototype + */ + skipOffscreen: true, + /** * @private * @param {HTMLElement | String} el <canvas> element to initialize instance on diff --git a/test/unit/object_geometry.js b/test/unit/object_geometry.js index 19af6229988..d86ceb4f092 100644 --- a/test/unit/object_geometry.js +++ b/test/unit/object_geometry.js @@ -331,6 +331,31 @@ ok(cObj.isOnScreen(), 'zooming out the object is again on screen'); }); + test('isOnScreen with object that include canvas', function(){ + var cObj = new fabric.Object( + { left: -10, top: -10, width: canvas.getWidth() + 100, height: canvas.getHeight(), strokeWidth: 0}); + canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; + cObj.canvas = canvas; + cObj.setCoords(); + equal(cObj.isOnScreen(), true, 'object is onScreen because it include the canvas'); + cObj.top = -1000; + cObj.left = -1000; + cObj.setCoords(); + equal(cObj.isOnScreen(), false, 'object is completely out of viewport'); + }); + + test('isOnScreen with object that is in top left corner of canvas', function(){ + var cObj = new fabric.Rect({left: -46.56, top: -9.23, width: 50,height: 50, angle: 314.57}); + canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; + cObj.canvas = canvas; + cObj.setCoords(); + ok(cObj.isOnScreen(), 'object is onScreen because it intersect a canvas line'); + cObj.top -= 20; + cObj.left -= 20; + cObj.setCoords(); + ok(!cObj.isOnScreen(), 'object is completely out of viewport'); + }); + test('calcTransformMatrix', function(){ var cObj = new fabric.Object({ width: 10, height: 15, strokeWidth: 0 }); ok(typeof cObj.calcTransformMatrix == 'function', 'calcTransformMatrix should exist');