diff --git a/CHANGELOG.md b/CHANGELOG.md index 929cc8a23f9..7f9b6893aa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [next] +- feat(): Add method toBlob. [#3283](https://github.com/fabricjs/fabric.js/issues/3283) + ## [6.5.4] - docs() perf(): Reorder caching conditions for most common scenario and docs fixes. [#10366](https://github.com/fabricjs/fabric.js/pull/10366) diff --git a/src/canvas/StaticCanvas.spec.ts b/src/canvas/StaticCanvas.spec.ts new file mode 100644 index 00000000000..aa2d8a5147f --- /dev/null +++ b/src/canvas/StaticCanvas.spec.ts @@ -0,0 +1,12 @@ +import { StaticCanvas } from './StaticCanvas'; + +describe('StaticCanvas', () => { + it('toBlob', async () => { + const canvas = new StaticCanvas(undefined, { width: 300, height: 300 }); + const blob = await canvas.toBlob({ + multiplier: 3, + }); + expect(blob).toBeInstanceOf(Blob); + expect(blob?.type).toBe('image/png'); + }); +}); diff --git a/src/canvas/StaticCanvas.ts b/src/canvas/StaticCanvas.ts index 8100043063c..3bfb8d84f75 100644 --- a/src/canvas/StaticCanvas.ts +++ b/src/canvas/StaticCanvas.ts @@ -26,7 +26,7 @@ import { } from '../util/animation/AnimationFrameProvider'; import { runningAnimations } from '../util/animation/AnimationRegistry'; import { uid } from '../util/internals/uid'; -import { createCanvasElementFor, toDataURL } from '../util/misc/dom'; +import { createCanvasElementFor, toBlob, toDataURL } from '../util/misc/dom'; import { invertTransform, transformPoint } from '../util/misc/matrix'; import type { EnlivenObjectOptions } from '../util/misc/objectEnlive'; import { @@ -1393,6 +1393,22 @@ export class StaticCanvas< quality, ); } + toBlob(options = {} as TDataUrlOptions): Promise { + const { + format = 'png', + quality = 1, + multiplier = 1, + enableRetinaScaling = false, + } = options; + const finalMultiplier = + multiplier * (enableRetinaScaling ? this.getRetinaScaling() : 1); + + return toBlob( + this.toCanvasElement(finalMultiplier, options), + format, + quality, + ); + } /** * Create a new HTMLCanvas element painted with the current canvas content. diff --git a/src/color/color.test.ts b/src/color/color.test.ts index b13c38d66f6..8fa6b6777b6 100644 --- a/src/color/color.test.ts +++ b/src/color/color.test.ts @@ -223,7 +223,7 @@ describe('test Color.fromHsla for color', () => { stringToParse: 'hsl( -450, 50%, 50%, .5)', expectedSource: [127, 64, 191, 0.5], }, - { + { name: 'fromHsla with Saturation 0', stringToParse: 'hsla(0, 0%, 50%, 1)', expectedSource: [128, 128, 128, 1], @@ -324,4 +324,4 @@ describe('parsing colors for color', () => { expect(colorUppercase).toBeInstanceOf(Color); expect(colorUppercase.getSource()).toEqual(expectedSource); }); -}); \ No newline at end of file +}); diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts index 775e0303f4d..4e427ba181d 100644 --- a/src/shapes/Object/Object.ts +++ b/src/shapes/Object/Object.ts @@ -31,6 +31,7 @@ import { createCanvasElement, createCanvasElementFor, toDataURL, + toBlob, } from '../../util/misc/dom'; import { invertTransform, qrDecompose } from '../../util/misc/matrix'; import { enlivenObjectEnlivables } from '../../util/misc/objectEnlive'; @@ -1403,6 +1404,13 @@ export class FabricObject< options.quality || 1, ); } + toBlob(options: toDataURLOptions = {}) { + return toBlob( + this.toCanvasElement(options), + options.format || 'png', + options.quality || 1, + ); + } /** * Returns true if any of the specified types is identical to the type of an instance diff --git a/src/util/index.ts b/src/util/index.ts index 2309eec076b..e17408d316f 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -51,6 +51,7 @@ export { createImage, copyCanvasElement, toDataURL, + toBlob, } from './misc/dom'; export { toFixed } from './misc/toFixed'; export { diff --git a/src/util/misc/dom.spec.ts b/src/util/misc/dom.spec.ts new file mode 100644 index 00000000000..52efccc3e46 --- /dev/null +++ b/src/util/misc/dom.spec.ts @@ -0,0 +1,22 @@ +import { toBlob, createCanvasElement } from './dom'; + +describe('DOM utils', () => { + it('toBlob without format', async () => { + const canvas = createCanvasElement(); + const blob = await toBlob(canvas); + expect(blob).toBeInstanceOf(Blob); + expect(blob?.type).toEqual('image/png'); + }); + it('toBlob png', async () => { + const canvas = createCanvasElement(); + const blob = await toBlob(canvas, 'png'); + expect(blob).toBeInstanceOf(Blob); + expect(blob?.type).toEqual('image/png'); + }); + it('toBlob jpeg', async () => { + const canvas = createCanvasElement(); + const blob = await toBlob(canvas, 'jpeg'); + expect(blob).toBeInstanceOf(Blob); + expect(blob?.type).toEqual('image/jpeg'); + }); +}); diff --git a/src/util/misc/dom.ts b/src/util/misc/dom.ts index f8a62e12c67..762fcabaf3a 100644 --- a/src/util/misc/dom.ts +++ b/src/util/misc/dom.ts @@ -61,3 +61,12 @@ export const isHTMLCanvas = ( ): canvas is HTMLCanvasElement => { return !!canvas && (canvas as HTMLCanvasElement).getContext !== undefined; }; + +export const toBlob = ( + canvasEl: HTMLCanvasElement, + format?: ImageFormat, + quality?: number, +) => + new Promise((resolve, _) => { + canvasEl.toBlob(resolve, `image/${format}`, quality); + }) as Promise;