UNPKG

fabric

Version:

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

1 lines 15.5 kB
{"version":3,"file":"Collection.mjs","names":[],"sources":["../../src/Collection.ts"],"sourcesContent":["import type { Constructor, TBBox } from './typedefs';\nimport { removeFromArray } from './util/internals/removeFromArray';\nimport { Point } from './Point';\nimport type { ActiveSelection } from './shapes/ActiveSelection';\nimport type { Group } from './shapes/Group';\nimport type { InteractiveFabricObject } from './shapes/Object/InteractiveObject';\nimport type { FabricObject } from './shapes/Object/FabricObject';\n\nexport const isCollection = (\n fabricObject?: FabricObject,\n): fabricObject is Group | ActiveSelection => {\n return !!fabricObject && Array.isArray((fabricObject as Group)._objects);\n};\n\nexport function createCollectionMixin<TBase extends Constructor>(Base: TBase) {\n class Collection extends Base {\n /**\n * @type {FabricObject[]}\n * @TODO needs to end up in the constructor too\n */\n _objects: FabricObject[] = [];\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _onObjectAdded(object: FabricObject) {\n // subclasses should override this method\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _onObjectRemoved(object: FabricObject) {\n // subclasses should override this method\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _onStackOrderChanged(object: FabricObject) {\n // subclasses should override this method\n }\n\n /**\n * Adds objects to collection\n * Objects should be instances of (or inherit from) FabricObject\n * @param {...FabricObject[]} objects to add\n * @returns {number} new array length\n */\n add(...objects: FabricObject[]): number {\n const size = this._objects.push(...objects);\n objects.forEach((object) => this._onObjectAdded(object));\n return size;\n }\n\n /**\n * Inserts an object into collection at specified index\n * @param {number} index Index to insert object at\n * @param {...FabricObject[]} objects Object(s) to insert\n * @returns {number} new array length\n */\n insertAt(index: number, ...objects: FabricObject[]) {\n this._objects.splice(index, 0, ...objects);\n objects.forEach((object) => this._onObjectAdded(object));\n return this._objects.length;\n }\n\n /**\n * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)\n * @private\n * @param {...FabricObject[]} objects objects to remove\n * @returns {FabricObject[]} removed objects\n */\n remove(...objects: FabricObject[]) {\n const array = this._objects,\n removed: FabricObject[] = [];\n objects.forEach((object) => {\n const index = array.indexOf(object);\n // only call onObjectRemoved if an object was actually removed\n if (index !== -1) {\n array.splice(index, 1);\n removed.push(object);\n this._onObjectRemoved(object);\n }\n });\n return removed;\n }\n\n /**\n * Executes given function for each object in this group\n * A simple shortcut for getObjects().forEach, before es6 was more complicated,\n * now is just a shortcut.\n * @param {Function} callback\n * Callback invoked with current object as first argument,\n * index - as second and an array of all objects - as third.\n */\n forEachObject(\n callback: (\n object: FabricObject,\n index: number,\n array: FabricObject[],\n ) => any,\n ) {\n this.getObjects().forEach((object, index, objects) =>\n callback(object, index, objects),\n );\n }\n\n /**\n * Returns an array of children objects of this instance\n * @param {...String} [types] When specified, only objects of these types are returned\n * @return {Array}\n */\n getObjects(...types: string[]) {\n if (types.length === 0) {\n return [...this._objects];\n }\n return this._objects.filter((o) => o.isType(...types));\n }\n\n /**\n * Returns object at specified index\n * @param {Number} index\n * @return {Object} object at index\n */\n item(index: number) {\n return this._objects[index];\n }\n\n /**\n * Returns true if collection contains no objects\n * @return {Boolean} true if collection is empty\n */\n isEmpty() {\n return this._objects.length === 0;\n }\n\n /**\n * Returns a size of a collection (i.e: length of an array containing its objects)\n * @return {Number} Collection size\n */\n size() {\n return this._objects.length;\n }\n\n /**\n * Returns true if collection contains an object.\\\n * **Prefer using {@link FabricObject#isDescendantOf} for performance reasons**\n * instead of `a.contains(b)` use `b.isDescendantOf(a)`\n * @param {Object} object Object to check against\n * @param {Boolean} [deep=false] `true` to check all descendants, `false` to check only `_objects`\n * @return {Boolean} `true` if collection contains an object\n */\n contains(object: FabricObject, deep?: boolean): boolean {\n if (this._objects.includes(object)) {\n return true;\n } else if (deep) {\n return this._objects.some(\n (obj) =>\n obj instanceof Collection &&\n (obj as unknown as Collection).contains(object, true),\n );\n }\n return false;\n }\n\n /**\n * Returns number representation of a collection complexity\n * @return {Number} complexity\n */\n complexity() {\n return this._objects.reduce((memo, current) => {\n memo += current.complexity ? current.complexity() : 0;\n return memo;\n }, 0);\n }\n\n /**\n * Moves an object or the objects of a multiple selection\n * to the bottom of the stack of drawn objects\n * @param {fabric.Object} object Object to send to back\n * @returns {boolean} true if change occurred\n */\n sendObjectToBack(object: FabricObject) {\n if (!object || object === this._objects[0]) {\n return false;\n }\n removeFromArray(this._objects, object);\n this._objects.unshift(object);\n this._onStackOrderChanged(object);\n return true;\n }\n\n /**\n * Moves an object or the objects of a multiple selection\n * to the top of the stack of drawn objects\n * @param {fabric.Object} object Object to send\n * @returns {boolean} true if change occurred\n */\n bringObjectToFront(object: FabricObject) {\n if (!object || object === this._objects[this._objects.length - 1]) {\n return false;\n }\n removeFromArray(this._objects, object);\n this._objects.push(object);\n this._onStackOrderChanged(object);\n return true;\n }\n\n /**\n * Moves an object or a selection down in stack of drawn objects\n * An optional parameter, `intersecting` allows to move the object in behind\n * the first intersecting object. Where intersection is calculated with\n * bounding box. If no intersection is found, there will not be change in the\n * stack.\n * @param {fabric.Object} object Object to send\n * @param {boolean} [intersecting] If `true`, send object behind next lower intersecting object\n * @returns {boolean} true if change occurred\n */\n sendObjectBackwards(object: FabricObject, intersecting?: boolean) {\n if (!object) {\n return false;\n }\n const idx = this._objects.indexOf(object);\n if (idx !== 0) {\n // if object is not on the bottom of stack\n const newIdx = this.findNewLowerIndex(object, idx, intersecting);\n removeFromArray(this._objects, object);\n this._objects.splice(newIdx, 0, object);\n this._onStackOrderChanged(object);\n return true;\n }\n return false;\n }\n\n /**\n * Moves an object or a selection up in stack of drawn objects\n * An optional parameter, intersecting allows to move the object in front\n * of the first intersecting object. Where intersection is calculated with\n * bounding box. If no intersection is found, there will not be change in the\n * stack.\n * @param {fabric.Object} object Object to send\n * @param {boolean} [intersecting] If `true`, send object in front of next upper intersecting object\n * @returns {boolean} true if change occurred\n */\n bringObjectForward(object: FabricObject, intersecting?: boolean) {\n if (!object) {\n return false;\n }\n const idx = this._objects.indexOf(object);\n if (idx !== this._objects.length - 1) {\n // if object is not on top of stack (last item in an array)\n const newIdx = this.findNewUpperIndex(object, idx, intersecting);\n removeFromArray(this._objects, object);\n this._objects.splice(newIdx, 0, object);\n this._onStackOrderChanged(object);\n return true;\n }\n return false;\n }\n\n /**\n * Moves an object to specified level in stack of drawn objects\n * @param {fabric.Object} object Object to send\n * @param {number} index Position to move to\n * @returns {boolean} true if change occurred\n */\n moveObjectTo(object: FabricObject, index: number) {\n if (object === this._objects[index]) {\n return false;\n }\n removeFromArray(this._objects, object);\n this._objects.splice(index, 0, object);\n this._onStackOrderChanged(object);\n return true;\n }\n\n findNewLowerIndex(\n object: FabricObject,\n idx: number,\n intersecting?: boolean,\n ) {\n let newIdx;\n\n if (intersecting) {\n newIdx = idx;\n // traverse down the stack looking for the nearest intersecting object\n for (let i = idx - 1; i >= 0; --i) {\n if (object.isOverlapping(this._objects[i])) {\n newIdx = i;\n break;\n }\n }\n } else {\n newIdx = idx - 1;\n }\n\n return newIdx;\n }\n\n findNewUpperIndex(\n object: FabricObject,\n idx: number,\n intersecting?: boolean,\n ) {\n let newIdx;\n\n if (intersecting) {\n newIdx = idx;\n // traverse up the stack looking for the nearest intersecting object\n for (let i = idx + 1; i < this._objects.length; ++i) {\n if (object.isOverlapping(this._objects[i])) {\n newIdx = i;\n break;\n }\n }\n } else {\n newIdx = idx + 1;\n }\n\n return newIdx;\n }\n\n /**\n * Given a bounding box, return all the objects of the collection that are contained in the bounding box.\n * If `includeIntersecting` is true, return also the objects that intersect the bounding box as well.\n * This is meant to work with selection. Is not a generic method.\n * @param {TBBox} bbox a bounding box in scene coordinates\n * @param {{ includeIntersecting?: boolean }} options an object with includeIntersecting\n * @returns array of objects contained in the bounding box, ordered from top to bottom stacking wise\n */\n collectObjects(\n { left, top, width, height }: TBBox,\n { includeIntersecting = true }: { includeIntersecting?: boolean } = {},\n ) {\n const objects: InteractiveFabricObject[] = [],\n tl = new Point(left, top),\n br = tl.add(new Point(width, height));\n\n // we iterate reverse order to collect top first in case of click.\n for (let i = this._objects.length - 1; i >= 0; i--) {\n const object = this._objects[i] as unknown as InteractiveFabricObject;\n if (\n object.selectable &&\n object.visible &&\n ((includeIntersecting && object.intersectsWithRect(tl, br)) ||\n object.isContainedWithinRect(tl, br) ||\n (includeIntersecting && object.containsPoint(tl)) ||\n (includeIntersecting && object.containsPoint(br)))\n ) {\n objects.push(object);\n }\n }\n\n return objects;\n }\n }\n\n // https://github.com/microsoft/TypeScript/issues/32080\n return Collection;\n}\n"],"mappings":"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"}