From 4ebcd4eddeac3132d6fdd87a06cdea57f5914b7f Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 4 Dec 2022 13:19:01 +0200 Subject: [PATCH 1/6] chore(): rm `fabric.filterBackend` => `getFilterBackend` prepare for dropping fabric NS --- src/filters/FilterBackend.ts | 13 ++++++------- src/shapes/image.class.ts | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/filters/FilterBackend.ts b/src/filters/FilterBackend.ts index 318ca8b8719..0ab0acd4d2e 100644 --- a/src/filters/FilterBackend.ts +++ b/src/filters/FilterBackend.ts @@ -6,6 +6,8 @@ import { webGLProbe } from './WebGLProbe'; export type FilterBackend = WebGLFilterBackend | Canvas2dFilterBackend; +let filterBackend: FilterBackend; + export function initFilterBackend(): FilterBackend { webGLProbe.queryWebGL(); if (config.enableGLFiltering && webGLProbe.isSupported(config.textureSize)) { @@ -15,14 +17,11 @@ export function initFilterBackend(): FilterBackend { } } -/** - * @todo refactor to a module w/o assigning to fabric - */ -export function getFilterBackend(): FilterBackend { - if (!fabric.filterBackend) { - fabric.filterBackend = initFilterBackend(); +export function getFilterBackend(strict = true): FilterBackend { + if (!filterBackend && strict) { + filterBackend = initFilterBackend(); } - return fabric.filterBackend; + return filterBackend; } fabric.Canvas2dFilterBackend = Canvas2dFilterBackend; diff --git a/src/shapes/image.class.ts b/src/shapes/image.class.ts index 49bf6a4dc17..84a68fb46ea 100644 --- a/src/shapes/image.class.ts +++ b/src/shapes/image.class.ts @@ -181,7 +181,7 @@ export class Image extends FabricObject { * Delete a single texture if in webgl mode */ removeTexture(key: string) { - const backend = fabric.filterBackend; + const backend = getFilterBackend(false); if (backend && backend.evictCachesForKey) { backend.evictCachesForKey(key); } From 5cbdea9faf9298f72e25bc54f8c099403aa90352 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 4 Dec 2022 13:24:28 +0200 Subject: [PATCH 2/6] Update FilterBackend.ts --- src/filters/FilterBackend.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/filters/FilterBackend.ts b/src/filters/FilterBackend.ts index 0ab0acd4d2e..cc7d9813623 100644 --- a/src/filters/FilterBackend.ts +++ b/src/filters/FilterBackend.ts @@ -17,6 +17,11 @@ export function initFilterBackend(): FilterBackend { } } +/** + * + * @param [strict] pass `true` to create the backend if it wasn't created yet (default behavior), + * pass `false` to get the backend ref without mutating it + */ export function getFilterBackend(strict = true): FilterBackend { if (!filterBackend && strict) { filterBackend = initFilterBackend(); From 243f79dcbac196e26a2db9bf9e68a50faddea272 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 4 Dec 2022 13:25:19 +0200 Subject: [PATCH 3/6] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7054d42c3c2..19341547bb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [next] +- chore(): rm `fabric.filterBackend` => `getFilterBackend` [#8487](https://github.com/fabricjs/fabric.js/pull/8487) - chore(): refactor `Object.__uid++` => `uid()` [#8482](https://github.com/fabricjs/fabric.js/pull/8482) - chore(TS): migrate object mixins to TS [#8414](https://github.com/fabricjs/fabric.js/pull/8414) - chore(TS): migrate filters [#8474](https://github.com/fabricjs/fabric.js/pull/8474) From 63a0a5c14bb517a06d01f80de0e167b3025839e1 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 4 Dec 2022 17:25:14 +0200 Subject: [PATCH 4/6] fix test --- src/filters/FilterBackend.ts | 1 + test/unit/image.js | 44 ++++++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/filters/FilterBackend.ts b/src/filters/FilterBackend.ts index cc7d9813623..5cec4a06853 100644 --- a/src/filters/FilterBackend.ts +++ b/src/filters/FilterBackend.ts @@ -29,6 +29,7 @@ export function getFilterBackend(strict = true): FilterBackend { return filterBackend; } +fabric.getFilterBackend = getFilterBackend; fabric.Canvas2dFilterBackend = Canvas2dFilterBackend; fabric.WebglFilterBackend = WebGLFilterBackend; fabric.initFilterBackend = initFilterBackend; diff --git a/test/unit/image.js b/test/unit/image.js index d6deedfab77..485695324ec 100644 --- a/test/unit/image.js +++ b/test/unit/image.js @@ -377,23 +377,33 @@ }); }); - QUnit.test('setElement resets the webgl cache', function(assert) { - var done = assert.async(); - var fabricBackend = fabric.filterBackend; - createImageObject(function(image) { - fabric.filterBackend = { - textureCache: {}, - evictCachesForKey: function(key) { - delete this.textureCache[key]; - } - }; - var elImage = _createImageElement(); - fabric.filterBackend.textureCache[image.cacheKey] = 'something'; - image.setElement(elImage); - assert.equal(fabric.filterBackend.textureCache[image.cacheKey], undefined); - fabric.filterBackend = fabricBackend; - done(); - }); + QUnit.test('setElement calls `removeTexture`', function (assert) { + const done = assert.async(); + const keys = []; + createImageObject((image) => { + image.cacheKey = 'TEST'; + // use sinon replace or something one day + image.removeTexture = (key) => keys.push(key); + image.setElement(_createImageElement()); + assert.deepEqual(keys, ['TEST', 'TEST_filtered'], 'should try to remove caches'); + done(); + }); + }); + + QUnit.test('setElement resets the webgl cache', function (assert) { + const backend = fabric.getFilterBackend(); + if (backend instanceof fabric.WebglFilterBackend) { + const done = assert.async(); + createImageObject((image) => { + backend.textureCache[image.cacheKey] = backend.createTexture(backend.gl, 50, 50); + assert.ok(backend.textureCache[image.cacheKey]); + image.setElement(_createImageElement()); + assert.equal(backend.textureCache[image.cacheKey], undefined); + done(); + }); + } else { + assert.expect(0); + } }); QUnit.test('crossOrigin', function(assert) { From a4dd0d699cd00ed80d45f42074830bb73d5252fa Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 5 Dec 2022 13:20:20 +0200 Subject: [PATCH 5/6] death to chainables --- src/static_canvas.class.ts | 72 +++++--------------------------------- test/unit/canvas.js | 18 +++++----- test/unit/canvas_static.js | 16 ++++----- 3 files changed, 26 insertions(+), 80 deletions(-) diff --git a/src/static_canvas.class.ts b/src/static_canvas.class.ts index e9097b70360..e05e79ee8f4 100644 --- a/src/static_canvas.class.ts +++ b/src/static_canvas.class.ts @@ -376,12 +376,9 @@ export class StaticCanvas extends createCollectionMixin( /** * Calculates canvas element offset relative to the document * This method is also attached as "resize" event handler of window - * @return {fabric.Canvas} instance - * @chainable */ calcOffset() { - this._offset = getElementOffset(this.lowerCanvasEl); - return this; + return (this._offset = getElementOffset(this.lowerCanvasEl)); } /** @@ -502,8 +499,6 @@ export class StaticCanvas extends createCollectionMixin( * @param {Object} [options] Options object * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions - * @return {fabric.Canvas} thisArg - * @chainable */ setDimensions( dimensions: Partial, @@ -534,8 +529,6 @@ export class StaticCanvas extends createCollectionMixin( if (!cssOnly) { this.requestRenderAll(); } - - return this; } /** @@ -543,9 +536,7 @@ export class StaticCanvas extends createCollectionMixin( * @private * @param {String} prop property (width|height) * @param {Number} value value to set property to - * @return {fabric.Canvas} instance * @todo subclass in canvas and handle upperCanvasEl there. - * @chainable true */ _setBackstoreDimension(prop: keyof TSize, value: number) { this.lowerCanvasEl[prop] = value; @@ -559,8 +550,6 @@ export class StaticCanvas extends createCollectionMixin( } this[prop] = value; - - return this; } /** @@ -568,9 +557,7 @@ export class StaticCanvas extends createCollectionMixin( * @private * @param {String} prop property (width|height) * @param {String} value value to set property to - * @return {fabric.Canvas} instance * @todo subclass in canvas and handle upperCanvasEl there. - * @chainable true */ _setCssDimension(prop: keyof TSize, value: string) { this.lowerCanvasEl.style[prop] = value; @@ -582,8 +569,6 @@ export class StaticCanvas extends createCollectionMixin( if (this.wrapperEl) { this.wrapperEl.style[prop] = value; } - - return this; } /** @@ -597,8 +582,6 @@ export class StaticCanvas extends createCollectionMixin( /** * Sets viewport transformation of this canvas instance * @param {Array} vpt a Canvas 2D API transform matrix - * @return {fabric.Canvas} instance - * @chainable true */ setViewportTransform(vpt: TMat2D) { const activeObject = this._activeObject, @@ -622,7 +605,6 @@ export class StaticCanvas extends createCollectionMixin( } this.calcViewportBoundaries(); this.renderOnAddRemove && this.requestRenderAll(); - return this; } /** @@ -632,8 +614,6 @@ export class StaticCanvas extends createCollectionMixin( * It has nothing to do with canvas center or visual center of the viewport. * @param {Point} point to zoom with respect to * @param {Number} value to set zoom to, less than 1 zooms out - * @return {fabric.Canvas} instance - * @chainable true */ zoomToPoint(point: Point, value: number) { // TODO: just change the scale, preserve other transformations @@ -645,17 +625,15 @@ export class StaticCanvas extends createCollectionMixin( const after = transformPoint(newPoint, vpt); vpt[4] += before.x - after.x; vpt[5] += before.y - after.y; - return this.setViewportTransform(vpt); + this.setViewportTransform(vpt); } /** * Sets zoom level of this canvas instance * @param {Number} value to set zoom to, less than 1 zooms out - * @return {fabric.Canvas} instance - * @chainable true */ setZoom(value: number) { - return this.zoomToPoint(new Point(0, 0), value); + this.zoomToPoint(new Point(0, 0), value); } /** @@ -693,12 +671,9 @@ export class StaticCanvas extends createCollectionMixin( /** * Clears specified context of canvas element * @param {CanvasRenderingContext2D} ctx Context to clear - * @return {fabric.Canvas} thisArg - * @chainable */ - clearContext(ctx: CanvasRenderingContext2D): StaticCanvas { + clearContext(ctx: CanvasRenderingContext2D) { ctx.clearRect(0, 0, this.width, this.height); - return this; } /** @@ -711,10 +686,8 @@ export class StaticCanvas extends createCollectionMixin( /** * Clears all contexts (background, main, top) of an instance - * @return {fabric.Canvas} thisArg - * @chainable */ - clear(): StaticCanvas { + clear() { this.remove(...this.getObjects()); this.backgroundImage = null; this.overlayImage = null; @@ -723,21 +696,17 @@ export class StaticCanvas extends createCollectionMixin( this.clearContext(this.contextContainer); this.fire('canvas:cleared'); this.renderOnAddRemove && this.requestRenderAll(); - return this; } /** * Renders the canvas - * @return {fabric.Canvas} instance - * @chainable */ - renderAll(): StaticCanvas { + renderAll() { this.cancelRequestedRender(); if (this.destroyed) { - return this; + return; } this.renderCanvas(this.contextContainer, this._objects); - return this; } /** @@ -747,8 +716,6 @@ export class StaticCanvas extends createCollectionMixin( * animationFrame stacking on to of each other * for an imperative rendering, use canvas.renderAll * @private - * @return {fabric.Canvas} instance - * @chainable */ renderAndReset() { this.nextRenderHandle = 0; @@ -759,14 +726,11 @@ export class StaticCanvas extends createCollectionMixin( * Append a renderAll request to next animation frame. * unless one is already in progress, in that case nothing is done * a boolean flag will avoid appending more. - * @return {fabric.Canvas} instance - * @chainable */ - requestRenderAll(): StaticCanvas { + requestRenderAll() { if (!this.nextRenderHandle && !this.disposed && !this.destroyed) { this.nextRenderHandle = requestAnimFrame(this.renderAndResetBound); } - return this; } /** @@ -805,8 +769,6 @@ export class StaticCanvas extends createCollectionMixin( * Renders background, objects, overlay and controls. * @param {CanvasRenderingContext2D} ctx * @param {Array} objects to render - * @return {fabric.Canvas} instance - * @chainable */ renderCanvas(ctx: CanvasRenderingContext2D, objects: FabricObject[]) { if (this.destroyed) { @@ -974,8 +936,6 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object horizontally in the canvas - * @param {FabricObject} object Object to center horizontally - * @return {fabric.Canvas} thisArg */ centerObjectH(object: FabricObject) { return this._centerObject( @@ -987,8 +947,6 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object vertically in the canvas * @param {FabricObject} object Object to center vertically - * @return {fabric.Canvas} thisArg - * @chainable */ centerObjectV(object: FabricObject) { return this._centerObject( @@ -1000,8 +958,6 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object vertically and horizontally in the canvas * @param {FabricObject} object Object to center vertically and horizontally - * @return {fabric.Canvas} thisArg - * @chainable */ centerObject(object: FabricObject) { return this._centerObject(object, this.getCenterPoint()); @@ -1010,8 +966,6 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object vertically and horizontally in the viewport * @param {FabricObject} object Object to center vertically and horizontally - * @return {fabric.Canvas} thisArg - * @chainable */ viewportCenterObject(object: FabricObject) { return this._centerObject(object, this.getVpCenter()); @@ -1020,8 +974,6 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object horizontally in the viewport, object.top is unchanged * @param {FabricObject} object Object to center vertically and horizontally - * @return {fabric.Canvas} thisArg - * @chainable */ viewportCenterObjectH(object: FabricObject) { return this._centerObject( @@ -1033,8 +985,6 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object Vertically in the viewport, object.top is unchanged * @param {FabricObject} object Object to center vertically and horizontally - * @return {fabric.Canvas} thisArg - * @chainable */ viewportCenterObjectV(object: FabricObject) { return this._centerObject( @@ -1046,7 +996,6 @@ export class StaticCanvas extends createCollectionMixin( /** * Calculate the point in canvas that correspond to the center of actual viewport. * @return {Point} vpCenter, viewport center - * @chainable */ getVpCenter(): Point { return transformPoint( @@ -1059,14 +1008,11 @@ export class StaticCanvas extends createCollectionMixin( * @private * @param {FabricObject} object Object to center * @param {Point} center Center point - * @return {fabric.Canvas} thisArg - * @chainable */ _centerObject(object: FabricObject, center: Point) { object.setXY(center, 'center', 'center'); object.setCoords(); this.renderOnAddRemove && this.requestRenderAll(); - return this; } /** @@ -1787,7 +1733,7 @@ export class StaticCanvas extends createCollectionMixin( * @return {String} string representation of an instance */ toString() { - return `#`; } diff --git a/test/unit/canvas.js b/test/unit/canvas.js index e003eccdc91..9ae56b39cc7 100644 --- a/test/unit/canvas.js +++ b/test/unit/canvas.js @@ -230,7 +230,7 @@ QUnit.test('calcOffset', function(assert) { assert.ok(typeof canvas.calcOffset === 'function', 'should respond to `calcOffset`'); - assert.equal(canvas.calcOffset(), canvas, 'should be chainable'); + assert.deepEqual(canvas.calcOffset(), { left: 0, top: 0 }, 'should retrun offset'); }); QUnit.test('add', function(assert) { @@ -744,13 +744,13 @@ QUnit.test('clearContext', function(assert) { assert.ok(typeof canvas.clearContext === 'function'); - assert.equal(canvas.clearContext(canvas.getContext()), canvas, 'should be chainable'); + canvas.clearContext(canvas.getContext()); }); QUnit.test('clear', function(assert) { assert.ok(typeof canvas.clear === 'function'); - assert.equal(canvas.clear(), canvas, 'should be chainable'); + canvas.clear(); assert.equal(canvas.getObjects().length, 0); }); @@ -1927,7 +1927,7 @@ makeRect({ left: 20, top: 20 }) ]); - assert.equal(canvas.setActiveObject(group), canvas, 'should be chainable'); + canvas.setActiveObject(group); assert.equal(canvas.getActiveObject(), group); }); @@ -1950,7 +1950,7 @@ QUnit.test('discardActiveObject on ActiveSelection', function(assert) { var group = new fabric.ActiveSelection([makeRect(), makeRect()]); canvas.setActiveObject(group); - assert.equal(canvas.discardActiveObject(), canvas, 'should be chainable'); + canvas.discardActiveObject(); assert.equal(canvas.getActiveObject(), null, 'removing active group sets it to null'); }); @@ -2021,10 +2021,10 @@ QUnit.test('toString', function(assert) { assert.ok(typeof canvas.toString === 'function'); - assert.equal(canvas.toString(), '#'); + assert.equal(canvas.toString(), '#'); canvas.add(makeRect()); - assert.equal(canvas.toString(), '#'); + assert.equal(canvas.toString(), '#'); }); QUnit.test('toSVG with active group', function(assert) { @@ -2135,7 +2135,7 @@ QUnit.test('getSetWidth', function(assert) { assert.ok(typeof canvas.getWidth === 'function'); assert.equal(canvas.getWidth(), 600); - assert.equal(canvas.setWidth(444), canvas, 'should be chainable'); + canvas.setWidth(444); assert.equal(canvas.getWidth(), 444); assert.equal(canvas.lowerCanvasEl.style.width, 444 + 'px'); }); @@ -2143,7 +2143,7 @@ QUnit.test('getSetHeight', function(assert) { assert.ok(typeof canvas.getHeight === 'function'); assert.equal(canvas.getHeight(), 600); - assert.equal(canvas.setHeight(765), canvas, 'should be chainable'); + canvas.setHeight(765); assert.equal(canvas.getHeight(), 765); assert.equal(canvas.lowerCanvasEl.style.height, 765 + 'px'); }); diff --git a/test/unit/canvas_static.js b/test/unit/canvas_static.js index 79cf35fe153..966e1486cf4 100644 --- a/test/unit/canvas_static.js +++ b/test/unit/canvas_static.js @@ -250,7 +250,7 @@ QUnit.test('calcOffset', function(assert) { assert.ok(typeof canvas.calcOffset === 'function', 'should respond to `calcOffset`'); - assert.equal(canvas.calcOffset(), canvas, 'should be chainable'); + assert.deepEqual(canvas.calcOffset(), { left: 0, top: 0 }, 'should retrun offset'); }); QUnit.test('add', function(assert) { @@ -496,7 +496,7 @@ QUnit.test('clearContext', function(assert) { assert.ok(typeof canvas.clearContext === 'function'); - assert.equal(canvas.clearContext(canvas.contextContainer), canvas, 'should be chainable'); + canvas.clearContext(canvas.contextContainer) }); QUnit.test('clear', function(assert) { @@ -516,7 +516,7 @@ rect3 = makeRect(); canvas.add(rect1, rect2, rect3); - assert.equal(canvas.clear(), canvas, 'should be chainable'); + canvas.clear(); assert.equal(canvas.getObjects().length, 0, 'clear remove all objects'); assert.strictEqual(objectsRemoved[0], rect1, 'clear should fire remove on previously added object'); assert.strictEqual(objectsRemoved[1], rect2, 'clear should fire remove on previously added object'); @@ -529,7 +529,7 @@ QUnit.test('renderAll', function(assert) { assert.ok(typeof canvas.renderAll === 'function'); - assert.equal(canvas, canvas.renderAll()); + canvas.renderAll(); }); // QUnit.test('setDimensions', function(assert) { @@ -1581,10 +1581,10 @@ QUnit.test('toString', function(assert) { assert.ok(typeof canvas.toString === 'function'); - assert.equal(canvas.toString(), '#'); + assert.equal(canvas.toString(), '#'); canvas.add(makeRect()); - assert.equal(canvas.toString(), '#'); + assert.equal(canvas.toString(), '#'); }); QUnit.test('clone', function(assert) { @@ -1623,7 +1623,7 @@ QUnit.test('getSetWidth', function(assert) { assert.ok(typeof canvas.getWidth === 'function'); assert.equal(canvas.getWidth(), 200); - assert.equal(canvas.setWidth(444), canvas, 'should be chainable'); + canvas.setWidth(444) assert.equal(canvas.getWidth(), 444); assert.equal(canvas.lowerCanvasEl.style.width, 444 + 'px'); }); @@ -1631,7 +1631,7 @@ QUnit.test('getSetHeight', function(assert) { assert.ok(typeof canvas.getHeight === 'function'); assert.equal(canvas.getHeight(), 200); - assert.equal(canvas.setHeight(765), canvas, 'should be chainable'); + canvas.setHeight(765) assert.equal(canvas.getHeight(), 765); assert.equal(canvas.lowerCanvasEl.style.height, 765 + 'px'); }); From 43df2cdae581a99a1adff056a7f1de9aef493a3b Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Mon, 5 Dec 2022 13:20:47 +0200 Subject: [PATCH 6/6] Revert "death to chainables" This reverts commit a4dd0d699cd00ed80d45f42074830bb73d5252fa. --- src/static_canvas.class.ts | 72 +++++++++++++++++++++++++++++++++----- test/unit/canvas.js | 18 +++++----- test/unit/canvas_static.js | 16 ++++----- 3 files changed, 80 insertions(+), 26 deletions(-) diff --git a/src/static_canvas.class.ts b/src/static_canvas.class.ts index e05e79ee8f4..e9097b70360 100644 --- a/src/static_canvas.class.ts +++ b/src/static_canvas.class.ts @@ -376,9 +376,12 @@ export class StaticCanvas extends createCollectionMixin( /** * Calculates canvas element offset relative to the document * This method is also attached as "resize" event handler of window + * @return {fabric.Canvas} instance + * @chainable */ calcOffset() { - return (this._offset = getElementOffset(this.lowerCanvasEl)); + this._offset = getElementOffset(this.lowerCanvasEl); + return this; } /** @@ -499,6 +502,8 @@ export class StaticCanvas extends createCollectionMixin( * @param {Object} [options] Options object * @param {Boolean} [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions * @param {Boolean} [options.cssOnly=false] Set the given dimensions only as css dimensions + * @return {fabric.Canvas} thisArg + * @chainable */ setDimensions( dimensions: Partial, @@ -529,6 +534,8 @@ export class StaticCanvas extends createCollectionMixin( if (!cssOnly) { this.requestRenderAll(); } + + return this; } /** @@ -536,7 +543,9 @@ export class StaticCanvas extends createCollectionMixin( * @private * @param {String} prop property (width|height) * @param {Number} value value to set property to + * @return {fabric.Canvas} instance * @todo subclass in canvas and handle upperCanvasEl there. + * @chainable true */ _setBackstoreDimension(prop: keyof TSize, value: number) { this.lowerCanvasEl[prop] = value; @@ -550,6 +559,8 @@ export class StaticCanvas extends createCollectionMixin( } this[prop] = value; + + return this; } /** @@ -557,7 +568,9 @@ export class StaticCanvas extends createCollectionMixin( * @private * @param {String} prop property (width|height) * @param {String} value value to set property to + * @return {fabric.Canvas} instance * @todo subclass in canvas and handle upperCanvasEl there. + * @chainable true */ _setCssDimension(prop: keyof TSize, value: string) { this.lowerCanvasEl.style[prop] = value; @@ -569,6 +582,8 @@ export class StaticCanvas extends createCollectionMixin( if (this.wrapperEl) { this.wrapperEl.style[prop] = value; } + + return this; } /** @@ -582,6 +597,8 @@ export class StaticCanvas extends createCollectionMixin( /** * Sets viewport transformation of this canvas instance * @param {Array} vpt a Canvas 2D API transform matrix + * @return {fabric.Canvas} instance + * @chainable true */ setViewportTransform(vpt: TMat2D) { const activeObject = this._activeObject, @@ -605,6 +622,7 @@ export class StaticCanvas extends createCollectionMixin( } this.calcViewportBoundaries(); this.renderOnAddRemove && this.requestRenderAll(); + return this; } /** @@ -614,6 +632,8 @@ export class StaticCanvas extends createCollectionMixin( * It has nothing to do with canvas center or visual center of the viewport. * @param {Point} point to zoom with respect to * @param {Number} value to set zoom to, less than 1 zooms out + * @return {fabric.Canvas} instance + * @chainable true */ zoomToPoint(point: Point, value: number) { // TODO: just change the scale, preserve other transformations @@ -625,15 +645,17 @@ export class StaticCanvas extends createCollectionMixin( const after = transformPoint(newPoint, vpt); vpt[4] += before.x - after.x; vpt[5] += before.y - after.y; - this.setViewportTransform(vpt); + return this.setViewportTransform(vpt); } /** * Sets zoom level of this canvas instance * @param {Number} value to set zoom to, less than 1 zooms out + * @return {fabric.Canvas} instance + * @chainable true */ setZoom(value: number) { - this.zoomToPoint(new Point(0, 0), value); + return this.zoomToPoint(new Point(0, 0), value); } /** @@ -671,9 +693,12 @@ export class StaticCanvas extends createCollectionMixin( /** * Clears specified context of canvas element * @param {CanvasRenderingContext2D} ctx Context to clear + * @return {fabric.Canvas} thisArg + * @chainable */ - clearContext(ctx: CanvasRenderingContext2D) { + clearContext(ctx: CanvasRenderingContext2D): StaticCanvas { ctx.clearRect(0, 0, this.width, this.height); + return this; } /** @@ -686,8 +711,10 @@ export class StaticCanvas extends createCollectionMixin( /** * Clears all contexts (background, main, top) of an instance + * @return {fabric.Canvas} thisArg + * @chainable */ - clear() { + clear(): StaticCanvas { this.remove(...this.getObjects()); this.backgroundImage = null; this.overlayImage = null; @@ -696,17 +723,21 @@ export class StaticCanvas extends createCollectionMixin( this.clearContext(this.contextContainer); this.fire('canvas:cleared'); this.renderOnAddRemove && this.requestRenderAll(); + return this; } /** * Renders the canvas + * @return {fabric.Canvas} instance + * @chainable */ - renderAll() { + renderAll(): StaticCanvas { this.cancelRequestedRender(); if (this.destroyed) { - return; + return this; } this.renderCanvas(this.contextContainer, this._objects); + return this; } /** @@ -716,6 +747,8 @@ export class StaticCanvas extends createCollectionMixin( * animationFrame stacking on to of each other * for an imperative rendering, use canvas.renderAll * @private + * @return {fabric.Canvas} instance + * @chainable */ renderAndReset() { this.nextRenderHandle = 0; @@ -726,11 +759,14 @@ export class StaticCanvas extends createCollectionMixin( * Append a renderAll request to next animation frame. * unless one is already in progress, in that case nothing is done * a boolean flag will avoid appending more. + * @return {fabric.Canvas} instance + * @chainable */ - requestRenderAll() { + requestRenderAll(): StaticCanvas { if (!this.nextRenderHandle && !this.disposed && !this.destroyed) { this.nextRenderHandle = requestAnimFrame(this.renderAndResetBound); } + return this; } /** @@ -769,6 +805,8 @@ export class StaticCanvas extends createCollectionMixin( * Renders background, objects, overlay and controls. * @param {CanvasRenderingContext2D} ctx * @param {Array} objects to render + * @return {fabric.Canvas} instance + * @chainable */ renderCanvas(ctx: CanvasRenderingContext2D, objects: FabricObject[]) { if (this.destroyed) { @@ -936,6 +974,8 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object horizontally in the canvas + * @param {FabricObject} object Object to center horizontally + * @return {fabric.Canvas} thisArg */ centerObjectH(object: FabricObject) { return this._centerObject( @@ -947,6 +987,8 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object vertically in the canvas * @param {FabricObject} object Object to center vertically + * @return {fabric.Canvas} thisArg + * @chainable */ centerObjectV(object: FabricObject) { return this._centerObject( @@ -958,6 +1000,8 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object vertically and horizontally in the canvas * @param {FabricObject} object Object to center vertically and horizontally + * @return {fabric.Canvas} thisArg + * @chainable */ centerObject(object: FabricObject) { return this._centerObject(object, this.getCenterPoint()); @@ -966,6 +1010,8 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object vertically and horizontally in the viewport * @param {FabricObject} object Object to center vertically and horizontally + * @return {fabric.Canvas} thisArg + * @chainable */ viewportCenterObject(object: FabricObject) { return this._centerObject(object, this.getVpCenter()); @@ -974,6 +1020,8 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object horizontally in the viewport, object.top is unchanged * @param {FabricObject} object Object to center vertically and horizontally + * @return {fabric.Canvas} thisArg + * @chainable */ viewportCenterObjectH(object: FabricObject) { return this._centerObject( @@ -985,6 +1033,8 @@ export class StaticCanvas extends createCollectionMixin( /** * Centers object Vertically in the viewport, object.top is unchanged * @param {FabricObject} object Object to center vertically and horizontally + * @return {fabric.Canvas} thisArg + * @chainable */ viewportCenterObjectV(object: FabricObject) { return this._centerObject( @@ -996,6 +1046,7 @@ export class StaticCanvas extends createCollectionMixin( /** * Calculate the point in canvas that correspond to the center of actual viewport. * @return {Point} vpCenter, viewport center + * @chainable */ getVpCenter(): Point { return transformPoint( @@ -1008,11 +1059,14 @@ export class StaticCanvas extends createCollectionMixin( * @private * @param {FabricObject} object Object to center * @param {Point} center Center point + * @return {fabric.Canvas} thisArg + * @chainable */ _centerObject(object: FabricObject, center: Point) { object.setXY(center, 'center', 'center'); object.setCoords(); this.renderOnAddRemove && this.requestRenderAll(); + return this; } /** @@ -1733,7 +1787,7 @@ export class StaticCanvas extends createCollectionMixin( * @return {String} string representation of an instance */ toString() { - return `#`; } diff --git a/test/unit/canvas.js b/test/unit/canvas.js index 9ae56b39cc7..e003eccdc91 100644 --- a/test/unit/canvas.js +++ b/test/unit/canvas.js @@ -230,7 +230,7 @@ QUnit.test('calcOffset', function(assert) { assert.ok(typeof canvas.calcOffset === 'function', 'should respond to `calcOffset`'); - assert.deepEqual(canvas.calcOffset(), { left: 0, top: 0 }, 'should retrun offset'); + assert.equal(canvas.calcOffset(), canvas, 'should be chainable'); }); QUnit.test('add', function(assert) { @@ -744,13 +744,13 @@ QUnit.test('clearContext', function(assert) { assert.ok(typeof canvas.clearContext === 'function'); - canvas.clearContext(canvas.getContext()); + assert.equal(canvas.clearContext(canvas.getContext()), canvas, 'should be chainable'); }); QUnit.test('clear', function(assert) { assert.ok(typeof canvas.clear === 'function'); - canvas.clear(); + assert.equal(canvas.clear(), canvas, 'should be chainable'); assert.equal(canvas.getObjects().length, 0); }); @@ -1927,7 +1927,7 @@ makeRect({ left: 20, top: 20 }) ]); - canvas.setActiveObject(group); + assert.equal(canvas.setActiveObject(group), canvas, 'should be chainable'); assert.equal(canvas.getActiveObject(), group); }); @@ -1950,7 +1950,7 @@ QUnit.test('discardActiveObject on ActiveSelection', function(assert) { var group = new fabric.ActiveSelection([makeRect(), makeRect()]); canvas.setActiveObject(group); - canvas.discardActiveObject(); + assert.equal(canvas.discardActiveObject(), canvas, 'should be chainable'); assert.equal(canvas.getActiveObject(), null, 'removing active group sets it to null'); }); @@ -2021,10 +2021,10 @@ QUnit.test('toString', function(assert) { assert.ok(typeof canvas.toString === 'function'); - assert.equal(canvas.toString(), '#'); + assert.equal(canvas.toString(), '#'); canvas.add(makeRect()); - assert.equal(canvas.toString(), '#'); + assert.equal(canvas.toString(), '#'); }); QUnit.test('toSVG with active group', function(assert) { @@ -2135,7 +2135,7 @@ QUnit.test('getSetWidth', function(assert) { assert.ok(typeof canvas.getWidth === 'function'); assert.equal(canvas.getWidth(), 600); - canvas.setWidth(444); + assert.equal(canvas.setWidth(444), canvas, 'should be chainable'); assert.equal(canvas.getWidth(), 444); assert.equal(canvas.lowerCanvasEl.style.width, 444 + 'px'); }); @@ -2143,7 +2143,7 @@ QUnit.test('getSetHeight', function(assert) { assert.ok(typeof canvas.getHeight === 'function'); assert.equal(canvas.getHeight(), 600); - canvas.setHeight(765); + assert.equal(canvas.setHeight(765), canvas, 'should be chainable'); assert.equal(canvas.getHeight(), 765); assert.equal(canvas.lowerCanvasEl.style.height, 765 + 'px'); }); diff --git a/test/unit/canvas_static.js b/test/unit/canvas_static.js index 966e1486cf4..79cf35fe153 100644 --- a/test/unit/canvas_static.js +++ b/test/unit/canvas_static.js @@ -250,7 +250,7 @@ QUnit.test('calcOffset', function(assert) { assert.ok(typeof canvas.calcOffset === 'function', 'should respond to `calcOffset`'); - assert.deepEqual(canvas.calcOffset(), { left: 0, top: 0 }, 'should retrun offset'); + assert.equal(canvas.calcOffset(), canvas, 'should be chainable'); }); QUnit.test('add', function(assert) { @@ -496,7 +496,7 @@ QUnit.test('clearContext', function(assert) { assert.ok(typeof canvas.clearContext === 'function'); - canvas.clearContext(canvas.contextContainer) + assert.equal(canvas.clearContext(canvas.contextContainer), canvas, 'should be chainable'); }); QUnit.test('clear', function(assert) { @@ -516,7 +516,7 @@ rect3 = makeRect(); canvas.add(rect1, rect2, rect3); - canvas.clear(); + assert.equal(canvas.clear(), canvas, 'should be chainable'); assert.equal(canvas.getObjects().length, 0, 'clear remove all objects'); assert.strictEqual(objectsRemoved[0], rect1, 'clear should fire remove on previously added object'); assert.strictEqual(objectsRemoved[1], rect2, 'clear should fire remove on previously added object'); @@ -529,7 +529,7 @@ QUnit.test('renderAll', function(assert) { assert.ok(typeof canvas.renderAll === 'function'); - canvas.renderAll(); + assert.equal(canvas, canvas.renderAll()); }); // QUnit.test('setDimensions', function(assert) { @@ -1581,10 +1581,10 @@ QUnit.test('toString', function(assert) { assert.ok(typeof canvas.toString === 'function'); - assert.equal(canvas.toString(), '#'); + assert.equal(canvas.toString(), '#'); canvas.add(makeRect()); - assert.equal(canvas.toString(), '#'); + assert.equal(canvas.toString(), '#'); }); QUnit.test('clone', function(assert) { @@ -1623,7 +1623,7 @@ QUnit.test('getSetWidth', function(assert) { assert.ok(typeof canvas.getWidth === 'function'); assert.equal(canvas.getWidth(), 200); - canvas.setWidth(444) + assert.equal(canvas.setWidth(444), canvas, 'should be chainable'); assert.equal(canvas.getWidth(), 444); assert.equal(canvas.lowerCanvasEl.style.width, 444 + 'px'); }); @@ -1631,7 +1631,7 @@ QUnit.test('getSetHeight', function(assert) { assert.ok(typeof canvas.getHeight === 'function'); assert.equal(canvas.getHeight(), 200); - canvas.setHeight(765) + assert.equal(canvas.setHeight(765), canvas, 'should be chainable'); assert.equal(canvas.getHeight(), 765); assert.equal(canvas.lowerCanvasEl.style.height, 765 + 'px'); });