UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

1 lines 8.96 kB
{"version":3,"file":"objectEnlive.mjs","names":[],"sources":["../../../../src/util/misc/objectEnlive.ts"],"sourcesContent":["import { noop } from '../../constants';\nimport type { FabricObject } from '../../shapes/Object/FabricObject';\nimport type {\n Abortable,\n Constructor,\n TCrossOrigin,\n TFiller,\n} from '../../typedefs';\nimport { createImage } from './dom';\nimport { classRegistry } from '../../ClassRegistry';\nimport type { BaseFilter } from '../../filters/BaseFilter';\nimport type { FabricObject as BaseFabricObject } from '../../shapes/Object/Object';\nimport { FabricError, SignalAbortedError } from '../internals/console';\nimport type { Shadow } from '../../Shadow';\n\nexport type LoadImageOptions = Abortable & {\n /**\n * cors value for the image loading, default to anonymous\n */\n crossOrigin?: TCrossOrigin;\n};\n\n/**\n * Loads image element from given url and resolve it, or catch.\n * @param {String} url URL representing an image\n * @param {LoadImageOptions} [options] image loading options\n * @returns {Promise<HTMLImageElement>} the loaded image.\n */\nexport const loadImage = (\n url: string,\n { signal, crossOrigin = null }: LoadImageOptions = {},\n) =>\n new Promise<HTMLImageElement>(function (resolve, reject) {\n if (signal && signal.aborted) {\n return reject(new SignalAbortedError('loadImage'));\n }\n const img = createImage();\n let abort: EventListenerOrEventListenerObject;\n if (signal) {\n abort = function (err: Event) {\n img.src = '';\n reject(err);\n };\n signal.addEventListener('abort', abort, { once: true });\n }\n const done = function () {\n img.onload = img.onerror = null;\n abort && signal?.removeEventListener('abort', abort);\n resolve(img);\n };\n if (!url) {\n done();\n return;\n }\n img.onload = done;\n img.onerror = function () {\n abort && signal?.removeEventListener('abort', abort);\n reject(new FabricError(`Error loading ${img.src}`));\n };\n crossOrigin && (img.crossOrigin = crossOrigin);\n img.src = url;\n });\n\nexport type EnlivenObjectOptions = Abortable & {\n /**\n * Method for further parsing of object elements,\n * called after each fabric object created.\n */\n reviver?: <\n T extends\n | BaseFabricObject\n | FabricObject\n | BaseFilter<string>\n | Shadow\n | TFiller,\n >(\n serializedObj: Record<string, any>,\n instance: T | undefined,\n error?: FabricError,\n ) => void | Promise<T>;\n};\n\n/**\n * @TODO type this correctly.\n * Creates corresponding fabric instances from their object representations\n * @param {Object[]} objects Objects to enliven\n * @param {EnlivenObjectOptions} [options]\n * @param {(serializedObj: object, instance: FabricObject) => any} [options.reviver] Method for further parsing of object elements,\n * called after each fabric object created.\n * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal\n * @returns {Promise<FabricObject[]>}\n */\nexport const enlivenObjects = <\n T extends\n | BaseFabricObject\n | FabricObject\n | BaseFilter<string>\n | Shadow\n | TFiller,\n>(\n objects: any[],\n { signal, reviver = noop }: EnlivenObjectOptions = {},\n) =>\n new Promise<T[]>((resolve, reject) => {\n const instances: T[] = [];\n signal && signal.addEventListener('abort', reject, { once: true });\n Promise.allSettled(\n objects.map((obj) =>\n classRegistry\n .getClass<\n Constructor<T> & {\n fromObject(options: any, context: Abortable): Promise<T>;\n }\n >(obj.type)\n .fromObject(obj, { signal }),\n ),\n )\n .then(async (elementsResult) => {\n for (const [index, result] of elementsResult.entries()) {\n if (result.status === 'fulfilled') {\n await reviver(objects[index], result.value);\n instances.push(result.value);\n }\n if (result.status === 'rejected') {\n const fallback = await reviver(\n objects[index],\n undefined,\n result.reason,\n );\n if (fallback) {\n instances.push(fallback as T);\n }\n }\n }\n resolve(instances);\n })\n .catch((error) => {\n // cleanup\n instances.forEach((instance) => {\n (instance as FabricObject).dispose &&\n (instance as FabricObject).dispose();\n });\n reject(error);\n })\n .finally(() => {\n signal && signal.removeEventListener('abort', reject);\n });\n });\n\n/**\n * Creates corresponding fabric instances residing in an object, e.g. `clipPath`\n * @param {Object} object with properties to enlive ( fill, stroke, clipPath, path )\n * @param {object} [options]\n * @param {AbortSignal} [options.signal] handle aborting, see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/signal\n * @returns {Promise<Record<string, FabricObject | TFiller | null>>} the input object with enlived values\n */\nexport const enlivenObjectEnlivables = <\n R = Record<string, FabricObject | TFiller | null>,\n>(\n serializedObject: any,\n { signal }: Abortable = {},\n) =>\n new Promise<R>((resolve, reject) => {\n const instances: (FabricObject | TFiller | Shadow)[] = [];\n signal && signal.addEventListener('abort', reject, { once: true });\n // enlive every possible property\n const promises = Object.values(serializedObject).map((value: any) => {\n if (!value) {\n return value;\n }\n /**\n * clipPath or shadow or gradient or text on a path or a pattern,\n * or the backgroundImage or overlayImage of canvas\n * If we have a type and there is a classe registered for it, we enlive it.\n * If there is no class registered for it we return the value as is\n * */\n if (value.type && classRegistry.has(value.type)) {\n return enlivenObjects<FabricObject | Shadow | TFiller>([value], {\n signal,\n }).then(([enlived]) => {\n instances.push(enlived);\n return enlived;\n });\n }\n return value;\n });\n const keys = Object.keys(serializedObject);\n Promise.all(promises)\n .then((enlived) => {\n return enlived.reduce((acc, instance, index) => {\n acc[keys[index]] = instance;\n return acc;\n }, {});\n })\n .then(resolve)\n .catch((error) => {\n // cleanup\n instances.forEach((instance: any) => {\n instance.dispose && instance.dispose();\n });\n reject(error);\n })\n .finally(() => {\n signal && signal.removeEventListener('abort', reject);\n });\n });\n"],"mappings":"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"}