diff --git a/.vscode/settings.json b/.vscode/settings.json index 1f6fc1db217..1b9e38a7ee9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,7 @@ "markdown.validate.ignoredLinks": [ // github links "../../**" - ] + ], + "editor.formatOnSave": true, + "editor.formatOnPaste": true } diff --git a/CHANGELOG.md b/CHANGELOG.md index 081ac731f58..14c4a7934a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [next] +\- chore(TS): Add declare in front of properties that are type definitions. [#8574](https://github.com/fabricjs/fabric.js/pull/8574) - refactor(Animation): BREAKING: Animation api reduction and semplification (byValue is removed, '+=' syntax is removed, callbacks fired 100%) [#8547](https://github.com/fabricjs/fabric.js/pull/8547) - feat(PolyControl): modify the shape of a poly with control points [#8556](https://github.com/fabricjs/fabric.js/pull/8556) - BREAKING: remove Object.stateful and Object.statefulCache [#8573](https://github.com/fabricjs/fabric.js/pull/8573) diff --git a/index.js b/index.js index 293b081091b..eadcb2f05a5 100644 --- a/index.js +++ b/index.js @@ -161,6 +161,7 @@ import { removeFromArray } from './src/util/internals/removeFromArray'; import { getRandomInt } from './src/util/internals/getRandomInt'; import { wrapElement } from './src/util/dom_misc'; import { request } from './src/util/dom_request'; +import { removeTransformMatrixForSvgParsing } from './src/util/transform_matrix_removal'; import { parseFontDeclaration } from './src/parser/parseFontDeclaration'; const util = { @@ -253,6 +254,8 @@ const util = { requestAnimFrame, cancelAnimFrame, classRegistry, + // for test compatibility. We don't want to export it. + removeTransformMatrixForSvgParsing, }; // CONTROLS UTILS diff --git a/src/canvas/canvas.class.ts b/src/canvas/canvas.class.ts index 657889199c8..aa9d7790e1c 100644 --- a/src/canvas/canvas.class.ts +++ b/src/canvas/canvas.class.ts @@ -159,7 +159,7 @@ export class SelectableCanvas< * @default * @since fabric 4.0 // changed name and default value */ - uniformScaling: boolean; + declare uniformScaling: boolean; /** * Indicates which key switches uniform scaling. @@ -173,7 +173,7 @@ export class SelectableCanvas< * @type ModifierKey * @default */ - uniScaleKey: TOptionalModifierKey; + declare uniScaleKey: TOptionalModifierKey; /** * When true, objects use center point as the origin of scale transformation. @@ -182,7 +182,7 @@ export class SelectableCanvas< * @type Boolean * @default */ - centeredScaling: boolean; + declare centeredScaling: boolean; /** * When true, objects use center point as the origin of rotate transformation. @@ -191,7 +191,7 @@ export class SelectableCanvas< * @type Boolean * @default */ - centeredRotation: boolean; + declare centeredRotation: boolean; /** * Indicates which key enable centered Transform @@ -202,7 +202,7 @@ export class SelectableCanvas< * @type ModifierKey * @default */ - centeredKey: TOptionalModifierKey; + declare centeredKey: TOptionalModifierKey; /** * Indicates which key enable alternate action on corner @@ -213,7 +213,7 @@ export class SelectableCanvas< * @type ModifierKey * @default */ - altActionKey: TOptionalModifierKey; + declare altActionKey: TOptionalModifierKey; /** * Indicates that canvas is interactive. This property should not be changed. @@ -227,7 +227,7 @@ export class SelectableCanvas< * @type Boolean * @default */ - selection: boolean; + declare selection: boolean; /** * Indicates which key or keys enable multiple click selection @@ -239,7 +239,7 @@ export class SelectableCanvas< * @type ModifierKey|ModifierKey[] * @default */ - selectionKey: TOptionalModifierKey | ModifierKey[]; + declare selectionKey: TOptionalModifierKey | ModifierKey[]; /** * Indicates which key enable alternative selection @@ -253,70 +253,70 @@ export class SelectableCanvas< * @type null|ModifierKey * @default */ - altSelectionKey: TOptionalModifierKey; + declare altSelectionKey: TOptionalModifierKey; /** * Color of selection * @type String * @default */ - selectionColor: string; + declare selectionColor: string; /** * Default dash array pattern * If not empty the selection border is dashed * @type Array */ - selectionDashArray: number[]; + declare selectionDashArray: number[]; /** * Color of the border of selection (usually slightly darker than color of selection itself) * @type String * @default */ - selectionBorderColor: string; + declare selectionBorderColor: string; /** * Width of a line used in object/group selection * @type Number * @default */ - selectionLineWidth: number; + declare selectionLineWidth: number; /** * Select only shapes that are fully contained in the dragged selection rectangle. * @type Boolean * @default */ - selectionFullyContained: boolean; + declare selectionFullyContained: boolean; /** * Default cursor value used when hovering over an object on canvas * @type CSSStyleDeclaration['cursor'] * @default move */ - hoverCursor: CSSStyleDeclaration['cursor']; + declare hoverCursor: CSSStyleDeclaration['cursor']; /** * Default cursor value used when moving an object on canvas * @type CSSStyleDeclaration['cursor'] * @default move */ - moveCursor: CSSStyleDeclaration['cursor']; + declare moveCursor: CSSStyleDeclaration['cursor']; /** * Default cursor value used for the entire canvas * @type String * @default default */ - defaultCursor: CSSStyleDeclaration['cursor']; + declare defaultCursor: CSSStyleDeclaration['cursor']; /** * Cursor value used during free drawing * @type String * @default crosshair */ - freeDrawingCursor: CSSStyleDeclaration['cursor']; + declare freeDrawingCursor: CSSStyleDeclaration['cursor']; /** * Cursor value used for disabled elements ( corners with disabled action ) @@ -324,28 +324,28 @@ export class SelectableCanvas< * @since 2.0.0 * @default not-allowed */ - notAllowedCursor: CSSStyleDeclaration['cursor']; + declare notAllowedCursor: CSSStyleDeclaration['cursor']; /** * Default element class that's given to wrapper (div) element of canvas * @type String * @default */ - containerClass: string; + declare containerClass: string; /** * When true, object detection happens on per-pixel basis rather than on per-bounding-box * @type Boolean * @default */ - perPixelTargetFind: boolean; + declare perPixelTargetFind: boolean; /** * Number of pixels around target pixel to tolerate (consider active) during object detection * @type Number * @default */ - targetFindTolerance: number; + declare targetFindTolerance: number; /** * When true, target detection is skipped. Target detection will return always undefined. @@ -356,7 +356,7 @@ export class SelectableCanvas< * @type Boolean * @default */ - skipTargetFind: boolean; + declare skipTargetFind: boolean; /** * When true, mouse events on canvas (mousedown/mousemove/mouseup) result in free drawing. @@ -366,7 +366,7 @@ export class SelectableCanvas< * @type Boolean * @default */ - isDrawingMode: boolean; + declare isDrawingMode: boolean; /** * Indicates whether objects should remain in current stack position when selected. @@ -374,7 +374,7 @@ export class SelectableCanvas< * @type Boolean * @default */ - preserveObjectStacking: boolean; + declare preserveObjectStacking: boolean; /** * Indicates if the right click on canvas can output the context menu or not @@ -382,7 +382,7 @@ export class SelectableCanvas< * @since 1.6.5 * @default */ - stopContextMenu: boolean; + declare stopContextMenu: boolean; /** * Indicates if the canvas can fire right click events @@ -390,7 +390,7 @@ export class SelectableCanvas< * @since 1.6.5 * @default */ - fireRightClick: boolean; + declare fireRightClick: boolean; /** * Indicates if the canvas can fire middle click events @@ -398,7 +398,7 @@ export class SelectableCanvas< * @since 1.7.8 * @default */ - fireMiddleClick: boolean; + declare fireMiddleClick: boolean; /** * Keep track of the subTargets for Mouse Events @@ -411,7 +411,7 @@ export class SelectableCanvas< * @type FabricObject | null * @private */ - _hoveredTarget?: FabricObject; + declare _hoveredTarget?: FabricObject; /** * hold the list of nested targets hovered @@ -459,7 +459,7 @@ export class SelectableCanvas< * @type CanvasRenderingContext2D * @private */ - contextCache: CanvasRenderingContext2D; + declare contextCache: CanvasRenderingContext2D; /** * During a mouse event we may need the pointer multiple times in multiple functions. @@ -468,7 +468,7 @@ export class SelectableCanvas< * We do this because there are some HTML DOM inspection functions to get the actual pointer coordinates * @type {Point} */ - protected _absolutePointer?: Point; + protected declare _absolutePointer?: Point; /** * During a mouse event we may need the pointer multiple times in multiple functions. @@ -477,7 +477,7 @@ export class SelectableCanvas< * We do this because there are some HTML DOM inspection functions to get the actual pointer coordinates * @type {Point} */ - protected _pointer?: Point; + protected declare _pointer?: Point; /** * During a mouse event we may need the target multiple times in multiple functions. @@ -485,17 +485,17 @@ export class SelectableCanvas< * lifespan. Every fabricJS mouse event create and delete the cache every time * @type {FabricObject} */ - protected _target?: FabricObject; - - upperCanvasEl: HTMLCanvasElement; - contextTop: CanvasRenderingContext2D; - wrapperEl: HTMLDivElement; - cacheCanvasEl: HTMLCanvasElement; - protected _isCurrentlyDrawing: boolean; - freeDrawingBrush?: BaseBrush; - _activeObject?: FabricObject; - _hasITextHandlers?: boolean; - _iTextInstances: (IText | Textbox)[]; + protected declare _target?: FabricObject; + + declare upperCanvasEl: HTMLCanvasElement; + declare contextTop: CanvasRenderingContext2D; + declare wrapperEl: HTMLDivElement; + declare cacheCanvasEl: HTMLCanvasElement; + protected declare _isCurrentlyDrawing: boolean; + declare freeDrawingBrush?: BaseBrush; + declare _activeObject?: FabricObject; + declare _hasITextHandlers?: boolean; + declare _iTextInstances: (IText | Textbox)[]; /** * Constructor * @param {HTMLCanvasElement | String} el canvas element to initialize instance on diff --git a/src/canvas/canvas_events.ts b/src/canvas/canvas_events.ts index 1f4e5d43a2b..1c2e5806b6c 100644 --- a/src/canvas/canvas_events.ts +++ b/src/canvas/canvas_events.ts @@ -74,46 +74,46 @@ export class Canvas extends SelectableCanvas { * @type Number * @private */ - mainTouchId: null | number; + declare mainTouchId: null | number; /** * When the option is enabled, PointerEvent is used instead of TPointerEvent. * @type Boolean * @default */ - enablePointerEvents: boolean; + declare enablePointerEvents: boolean; /** * an internal flag that is used to remember if we already bound the events * @type Boolean * @private */ - private eventsBound: boolean; + private declare eventsBound: boolean; /** * Holds a reference to a setTimeout timer for event synchronization * @type number * @private */ - private _willAddMouseDown: number; + private declare _willAddMouseDown: number; /** * Holds a reference to an object on the canvas that is receiving the drag over event. * @type FabricObject * @private */ - private _draggedoverTarget?: FabricObject; + private declare _draggedoverTarget?: FabricObject; /** * Holds a reference to an object on the canvas from where the drag operation started * @type FabricObject * @private */ - private _dragSource?: FabricObject; + private declare _dragSource?: FabricObject; - currentTarget?: FabricObject; + declare currentTarget?: FabricObject; - currentSubTargets?: FabricObject[]; + declare currentSubTargets?: FabricObject[]; /** * Holds a reference to a pointer during mousedown to compare on mouse up and determine @@ -121,7 +121,7 @@ export class Canvas extends SelectableCanvas { * @type FabricObject * @private */ - _previousPointer: Point; + declare _previousPointer: Point; /** * Adds mouse listeners to canvas diff --git a/src/canvas/static_canvas.class.ts b/src/canvas/static_canvas.class.ts index 76d422845d5..fbd3ebfc6e5 100644 --- a/src/canvas/static_canvas.class.ts +++ b/src/canvas/static_canvas.class.ts @@ -87,7 +87,7 @@ export class StaticCanvas< * @type {(String|TFiller)} * @default */ - backgroundColor: TFiller | string; + declare backgroundColor: TFiller | string; /** * Background image of canvas instance. @@ -97,7 +97,7 @@ export class StaticCanvas< * @type FabricObject * @default */ - backgroundImage: FabricObject | null; + declare backgroundImage: FabricObject | null; /** * Overlay color of canvas instance. @@ -105,7 +105,7 @@ export class StaticCanvas< * @type {(String|TFiller)} * @default */ - overlayColor: TFiller | string; + declare overlayColor: TFiller | string; /** * Overlay image of canvas instance. @@ -115,7 +115,7 @@ export class StaticCanvas< * @type FabricObject * @default */ - overlayImage: FabricObject | null; + declare overlayImage: FabricObject | null; /** * Indicates whether toObject/toDatalessObject should include default values @@ -123,7 +123,7 @@ export class StaticCanvas< * @type Boolean * @default */ - includeDefaultValues: boolean; + declare includeDefaultValues: boolean; /** * Indicates whether {@link add}, {@link insertAt} and {@link remove}, @@ -135,28 +135,28 @@ export class StaticCanvas< * @type Boolean * @default */ - renderOnAddRemove: boolean; + declare renderOnAddRemove: boolean; /** * Indicates whether object controls (borders/controls) are rendered above overlay image * @type Boolean * @default */ - controlsAboveOverlay: boolean; + declare controlsAboveOverlay: boolean; /** * Indicates whether the browser can be scrolled when using a touchscreen and dragging on the canvas * @type Boolean * @default */ - allowTouchScrolling: boolean; + declare allowTouchScrolling: boolean; /** * Indicates whether this canvas will use image smoothing, this is on by default in browsers * @type Boolean * @default */ - imageSmoothingEnabled: boolean; + declare imageSmoothingEnabled: boolean; /** * The transformation (a Canvas 2D API transform matrix) which focuses the viewport @@ -167,7 +167,7 @@ export class StaticCanvas< * canvas.viewportTransform = [0.7, 0, 0, 0.7, 50, 50]; * @default */ - viewportTransform: TMat2D; + declare viewportTransform: TMat2D; /** * if set to false background image is not affected by viewport transform @@ -176,7 +176,7 @@ export class StaticCanvas< * @todo we should really find a different way to do this * @default */ - backgroundVpt: boolean; + declare backgroundVpt: boolean; /** * if set to false overlya image is not affected by viewport transform @@ -185,14 +185,14 @@ export class StaticCanvas< * @todo we should really find a different way to do this * @default */ - overlayVpt: boolean; + declare overlayVpt: boolean; /** * When true, canvas is scaled by devicePixelRatio for better rendering on retina screens * @type Boolean * @default */ - enableRetinaScaling: boolean; + declare enableRetinaScaling: boolean; /** * Describe canvas element extension over design @@ -202,7 +202,7 @@ export class StaticCanvas< * of canvas element in plain untrasformed coordinates * The coordinates get updated with @method calcViewportBoundaries. */ - vptCoords: TCornerPoint; + declare vptCoords: TCornerPoint; /** * Based on vptCoords and object.aCoords, skip rendering of objects that @@ -213,7 +213,7 @@ export class StaticCanvas< * @type Boolean * @default */ - skipOffscreen: boolean; + declare skipOffscreen: boolean; /** * a fabricObject that, without stroke define a clipping area with their shape. filled in black @@ -222,65 +222,70 @@ export class StaticCanvas< * clipPath will clip away controls, if you do not want this to happen use controlsAboveOverlay = true * @type FabricObject */ - clipPath: FabricObject; + declare clipPath: FabricObject; /** * A reference to the canvas actual HTMLCanvasElement. * Can be use to read the raw pixels, but never write or manipulate * @type HTMLCanvasElement */ - lowerCanvasEl: HTMLCanvasElement; + declare lowerCanvasEl: HTMLCanvasElement; - contextContainer: CanvasRenderingContext2D; + declare contextContainer: CanvasRenderingContext2D; /** * Width in virtual/logical pixels of the canvas. * The canvas can be larger than width if retina scaling is active * @type number */ - width: number; + declare width: number; /** * Height in virtual/logical pixels of the canvas. * The canvas can be taller than width if retina scaling is active * @type height */ - height: number; + declare height: number; /** * If true the Canvas is in the process or has been disposed/destroyed. * No more rendering operation will be executed on this canvas. * @type boolean */ - destroyed?: boolean; + declare destroyed?: boolean; /** * Started the process of disposing but not done yet. * WIll likely complete the render cycle already scheduled but stopping adding more. * @type boolean */ - disposed?: boolean; + declare disposed?: boolean; /** * Keeps a copy of the canvas style before setting retina scaling and other potions * in order to return it to original state on dispose * @type string */ - _originalCanvasStyle?: string; + declare _originalCanvasStyle?: string; - renderAndResetBound: () => void; - requestRenderAllBound: () => void; + declare renderAndResetBound: () => void; + declare requestRenderAllBound: () => void; - _offset: { left: number; top: number }; - protected hasLostContext: boolean; - protected nextRenderHandle: number; + declare _offset: { left: number; top: number }; + protected declare hasLostContext: boolean; + protected declare nextRenderHandle: number; // reference to - protected __cleanupTask?: { + protected declare __cleanupTask?: { (): void; kill: (reason?: any) => void; }; + constructor(el: string | HTMLCanvasElement, options = {}) { + super(); + this._init(el, options); + } + add(...objects: FabricObject[]) { const size = super.add(...objects); objects.length > 0 && this.renderOnAddRemove && this.requestRenderAll(); @@ -325,11 +330,6 @@ export class StaticCanvas< this.renderOnAddRemove && this.requestRenderAll(); } - constructor(el: string | HTMLCanvasElement, options = {}) { - super(); - this._init(el, options); - } - /** * @private * @param {HTMLCanvasElement | String} el element to initialize instance on @@ -1148,7 +1148,7 @@ export class StaticCanvas< * @type Boolean * @default */ - svgViewportTransformation: boolean; + declare svgViewportTransformation: boolean; /** * Returns SVG representation of canvas diff --git a/src/filters/WebGLProbe.ts b/src/filters/WebGLProbe.ts index 8178ca51be9..19e3cd7d416 100644 --- a/src/filters/WebGLProbe.ts +++ b/src/filters/WebGLProbe.ts @@ -11,9 +11,9 @@ export enum WebGLPrecision { * Lazy initialize WebGL constants */ class WebGLProbe { - maxTextureSize?: number; + declare maxTextureSize?: number; - webGLPrecision: WebGLPrecision | undefined; + declare webGLPrecision: WebGLPrecision | undefined; /** * Tests if webgl supports certain precision diff --git a/src/filters/base_filter.class.ts b/src/filters/base_filter.class.ts index 960e6e85c37..77a38f71c56 100644 --- a/src/filters/base_filter.class.ts +++ b/src/filters/base_filter.class.ts @@ -25,15 +25,15 @@ export abstract class AbstractBaseFilter { * @param {String} type * @default */ - type: string; + declare type: string; /** * Array of attributes to send with buffers. do not modify * @private */ - vertexSource: string; + declare vertexSource: string; - fragmentSource: T; + declare fragmentSource: T; /** * Name of the parameter that can be changed in the filter. @@ -41,7 +41,7 @@ export abstract class AbstractBaseFilter { * mainParameter * @private */ - mainParameter?: keyof this; + declare mainParameter?: keyof this; /** * Constructor diff --git a/src/filters/blendcolor_filter.class.ts b/src/filters/blendcolor_filter.class.ts index 137a21dfc3d..b6f018c4a48 100644 --- a/src/filters/blendcolor_filter.class.ts +++ b/src/filters/blendcolor_filter.class.ts @@ -28,9 +28,9 @@ export class BlendColor extends AbstractBaseFilter> { * @type String * @default **/ - color: string; + declare color: string; - mode: + declare mode: | 'multiply' | 'add' | 'diff' @@ -48,7 +48,7 @@ export class BlendColor extends AbstractBaseFilter> { * @type Number * @default **/ - alpha: number; + declare alpha: number; /** * build the fragment source for the filters, joining the common part with diff --git a/src/filters/blendimage_filter.class.ts b/src/filters/blendimage_filter.class.ts index c1c72c2f780..6c779e71784 100644 --- a/src/filters/blendimage_filter.class.ts +++ b/src/filters/blendimage_filter.class.ts @@ -33,15 +33,15 @@ export class BlendImage extends AbstractBaseFilter> { * Color to make the blend operation with. default to a reddish color since black or white * gives always strong result. **/ - image: Image; + declare image: Image; - mode: 'multiply' | 'mask'; + declare mode: 'multiply' | 'mask'; /** * alpha value. represent the strength of the blend image operation. * not implemented. **/ - alpha: number; + declare alpha: number; getCacheKey() { return `${this.type}_${this.mode}`; diff --git a/src/filters/blur_filter.class.ts b/src/filters/blur_filter.class.ts index 851cb6349a0..9c4dde3f966 100644 --- a/src/filters/blur_filter.class.ts +++ b/src/filters/blur_filter.class.ts @@ -28,10 +28,10 @@ export class Blur extends BaseFilter { * @type Number * @default */ - blur: number; + declare blur: number; - horizontal: boolean; - aspectRatio: number; + declare horizontal: boolean; + declare aspectRatio: number; applyTo(options: TWebGLPipelineState | T2DPipelineState) { if (isWebGLPipelineState(options)) { diff --git a/src/filters/brightness_filter.class.ts b/src/filters/brightness_filter.class.ts index bd96376da9f..51a213a2595 100644 --- a/src/filters/brightness_filter.class.ts +++ b/src/filters/brightness_filter.class.ts @@ -19,7 +19,7 @@ export class Brightness extends BaseFilter { * @param {Number} brightness * @default */ - brightness: number; + declare brightness: number; /** * Apply the Brightness operation to a Uint8ClampedArray representing the pixels of an image. diff --git a/src/filters/colormatrix_filter.class.ts b/src/filters/colormatrix_filter.class.ts index aa4e212299c..eab3bdc8edd 100644 --- a/src/filters/colormatrix_filter.class.ts +++ b/src/filters/colormatrix_filter.class.ts @@ -27,7 +27,7 @@ export class ColorMatrix extends BaseFilter { * @param {Array} matrix array of 20 numbers. * @default */ - matrix: number[]; + declare matrix: number[]; /** * Lock the colormatrix on the color part, skipping alpha, mainly for non webgl scenario @@ -35,7 +35,7 @@ export class ColorMatrix extends BaseFilter { * @type Boolean * @default true */ - colorsOnly: boolean; + declare colorsOnly: boolean; setOptions({ matrix, ...options }: Record) { if (matrix) { diff --git a/src/filters/composed_filter.class.ts b/src/filters/composed_filter.class.ts index 9e1a5e1cd25..d581d277d6e 100644 --- a/src/filters/composed_filter.class.ts +++ b/src/filters/composed_filter.class.ts @@ -16,7 +16,7 @@ export class Composed extends BaseFilter { /** * A non sparse array of filters to apply */ - subFilters: AbstractBaseFilter[]; + declare subFilters: AbstractBaseFilter[]; constructor({ subFilters = [], diff --git a/src/filters/contrast_filter.class.ts b/src/filters/contrast_filter.class.ts index cd30b7fe781..6b3c4dc0d13 100644 --- a/src/filters/contrast_filter.class.ts +++ b/src/filters/contrast_filter.class.ts @@ -18,7 +18,7 @@ export class Contrast extends BaseFilter { * @param {Number} contrast * @default 0 */ - contrast: number; + declare contrast: number; /** * Apply the Contrast operation to a Uint8Array representing the pixels of an image. diff --git a/src/filters/convolute_filter.class.ts b/src/filters/convolute_filter.class.ts index 0ff18f1b1ba..70b0142d9c2 100644 --- a/src/filters/convolute_filter.class.ts +++ b/src/filters/convolute_filter.class.ts @@ -47,12 +47,12 @@ export class Convolute extends AbstractBaseFilter> { /* * Opaque value (true/false) */ - opaque: boolean; + declare opaque: boolean; /* * matrix for the filter, max 9x9 */ - matrix: number[]; + declare matrix: number[]; getCacheKey() { return `${this.type}_${Math.sqrt(this.matrix.length)}_${ diff --git a/src/filters/filter_boilerplate.ts b/src/filters/filter_boilerplate.ts index ea415c6f5fe..4d27eb9b07c 100644 --- a/src/filters/filter_boilerplate.ts +++ b/src/filters/filter_boilerplate.ts @@ -19,7 +19,7 @@ export class MyFilter extends BaseFilter { * @param {Number} myParameter * @default */ - myParameter: number; + declare myParameter: number; /** * Apply the MyFilter operation to a Uint8ClampedArray representing the pixels of an image. diff --git a/src/filters/gamma_filter.class.ts b/src/filters/gamma_filter.class.ts index 1659f7b3b67..25212b95ab3 100644 --- a/src/filters/gamma_filter.class.ts +++ b/src/filters/gamma_filter.class.ts @@ -20,8 +20,8 @@ export class Gamma extends BaseFilter { * @param {Array} gamma * @default */ - gamma: GammaInput; - rgbValues?: { + declare gamma: GammaInput; + declare rgbValues?: { r: Uint8Array; g: Uint8Array; b: Uint8Array; diff --git a/src/filters/grayscale_filter.class.ts b/src/filters/grayscale_filter.class.ts index 7bfdcd293f6..3a72d1bcf00 100644 --- a/src/filters/grayscale_filter.class.ts +++ b/src/filters/grayscale_filter.class.ts @@ -11,7 +11,7 @@ import { classRegistry } from '../util/class_registry'; * object.applyFilters(); */ export class Grayscale extends AbstractBaseFilter> { - mode: 'average' | 'lightness' | 'luminosity'; + declare mode: 'average' | 'lightness' | 'luminosity'; /** * Apply the Grayscale operation to a Uint8Array representing the pixels of an image. diff --git a/src/filters/hue_rotation.class.ts b/src/filters/hue_rotation.class.ts index 096d7dc4eb9..79027bc8b30 100644 --- a/src/filters/hue_rotation.class.ts +++ b/src/filters/hue_rotation.class.ts @@ -19,7 +19,7 @@ export class HueRotation extends ColorMatrix { /** * HueRotation value, from -1 to 1. */ - rotation: number; + declare rotation: number; calculateMatrix() { const rad = this.rotation * Math.PI, diff --git a/src/filters/invert_filter.class.ts b/src/filters/invert_filter.class.ts index 7cb5001fe1a..5544b5cf31c 100644 --- a/src/filters/invert_filter.class.ts +++ b/src/filters/invert_filter.class.ts @@ -15,14 +15,14 @@ export class Invert extends BaseFilter { * @param {Boolean} alpha * @default **/ - alpha: boolean; + declare alpha: boolean; /** * Filter invert. if false, does nothing * @param {Boolean} invert * @default */ - invert: boolean; + declare invert: boolean; /** * Apply the Invert operation to a Uint8Array representing the pixels of an image. diff --git a/src/filters/noise_filter.class.ts b/src/filters/noise_filter.class.ts index 48bb338aa94..a9ecd292a28 100644 --- a/src/filters/noise_filter.class.ts +++ b/src/filters/noise_filter.class.ts @@ -19,7 +19,7 @@ export class Noise extends BaseFilter { * @param {Number} noise * @default */ - noise: number; + declare noise: number; /** * Apply the Brightness operation to a Uint8ClampedArray representing the pixels of an image. diff --git a/src/filters/pixelate_filter.class.ts b/src/filters/pixelate_filter.class.ts index 80f8871198e..0c4108b22c2 100644 --- a/src/filters/pixelate_filter.class.ts +++ b/src/filters/pixelate_filter.class.ts @@ -13,7 +13,7 @@ import { classRegistry } from '../util/class_registry'; * object.applyFilters(); */ export class Pixelate extends BaseFilter { - blocksize: number; + declare blocksize: number; /** * Apply the Pixelate operation to a Uint8ClampedArray representing the pixels of an image. diff --git a/src/filters/removecolor_filter.class.ts b/src/filters/removecolor_filter.class.ts index e9e730a0236..e34ba3e71c9 100644 --- a/src/filters/removecolor_filter.class.ts +++ b/src/filters/removecolor_filter.class.ts @@ -20,19 +20,19 @@ export class RemoveColor extends BaseFilter { * @param {String} type * @default */ - color: string; + declare color: string; /** * distance to actual color, as value up or down from each r,g,b * between 0 and 1 **/ - distance: number; + declare distance: number; /** * For color to remove inside distance, use alpha channel for a smoother deletion * NOT IMPLEMENTED YET **/ - useAlpha: boolean; + declare useAlpha: boolean; /** * Applies filter to canvas element diff --git a/src/filters/resize_filter.class.ts b/src/filters/resize_filter.class.ts index a247bcaa407..bb9987a6bad 100644 --- a/src/filters/resize_filter.class.ts +++ b/src/filters/resize_filter.class.ts @@ -20,30 +20,30 @@ export class Resize extends BaseFilter { * bilinear, hermite, sliceHack, lanczos. * @default */ - resizeType: 'bilinear' | 'hermite' | 'sliceHack' | 'lanczos'; + declare resizeType: 'bilinear' | 'hermite' | 'sliceHack' | 'lanczos'; /** * Scale factor for resizing, x axis * @param {Number} scaleX * @default */ - scaleX: number; + declare scaleX: number; /** * Scale factor for resizing, y axis * @param {Number} scaleY * @default */ - scaleY: number; + declare scaleY: number; /** * LanczosLobes parameter for lanczos filter, valid for resizeType lanczos * @param {Number} lanczosLobes * @default */ - lanczosLobes: number; + declare lanczosLobes: number; - fragmentSourceTOP: string; + declare fragmentSourceTOP: string; /** * Return WebGL uniform locations for this filter's shader. diff --git a/src/filters/saturate_filter.class.ts b/src/filters/saturate_filter.class.ts index b7d21056cc1..8a390877c38 100644 --- a/src/filters/saturate_filter.class.ts +++ b/src/filters/saturate_filter.class.ts @@ -21,7 +21,7 @@ export class Saturation extends BaseFilter { * @param {Number} saturation * @default */ - saturation: number; + declare saturation: number; /** * Apply the Saturation operation to a Uint8ClampedArray representing the pixels of an image. diff --git a/src/filters/vibrance_filter.class.ts b/src/filters/vibrance_filter.class.ts index 05d799f6721..0ac78018033 100644 --- a/src/filters/vibrance_filter.class.ts +++ b/src/filters/vibrance_filter.class.ts @@ -21,7 +21,7 @@ export class Vibrance extends BaseFilter { * @param {Number} vibrance * @default */ - vibrance: number; + declare vibrance: number; /** * Apply the Vibrance operation to a Uint8ClampedArray representing the pixels of an image. diff --git a/src/filters/webgl_backend.class.ts b/src/filters/webgl_backend.class.ts index 77e738dcb48..5f9dbb4a4e9 100644 --- a/src/filters/webgl_backend.class.ts +++ b/src/filters/webgl_backend.class.ts @@ -4,7 +4,7 @@ import { createCanvasElement } from '../util/misc/dom'; import { TWebGLPipelineState, TProgramCache, TTextureCache } from './typedefs'; export class WebGLFilterBackend { - tileSize: number; + declare tileSize: number; /** * Define ... @@ -15,29 +15,29 @@ export class WebGLFilterBackend { * If GLPut data is the fastest operation, or if forced, this buffer will be used * to transfer the data back in the 2d logic **/ - imageBuffer?: ArrayBuffer; + declare imageBuffer?: ArrayBuffer; - canvas: HTMLCanvasElement; + declare canvas: HTMLCanvasElement; /** * The Webgl context that will execute the operations for filtering **/ - gl: WebGLRenderingContext; + declare gl: WebGLRenderingContext; /** * Keyed map for shader cache **/ - programCache: TProgramCache; + declare programCache: TProgramCache; /** * Keyed map for texture cache **/ - textureCache: TTextureCache; + declare textureCache: TTextureCache; /** * Contains GPU info for debug **/ - gpuInfo: any; + declare gpuInfo: any; /** * Experimental. This object is a sort of repository of help layers used to avoid diff --git a/src/gradient/gradient.class.ts b/src/gradient/gradient.class.ts index 9ace832309d..c5aa0cf71d3 100644 --- a/src/gradient/gradient.class.ts +++ b/src/gradient/gradient.class.ts @@ -38,14 +38,14 @@ export class Gradient< * @type Number * @default 0 */ - offsetX = 0; + declare offsetX: number; /** * Vertical offset for aligning gradients coming from SVG when outside pathgroups * @type Number * @default 0 */ - offsetY = 0; + declare offsetY: number; /** * A transform matrix to apply to the gradient before painting. @@ -55,7 +55,7 @@ export class Gradient< * @type Number[] * @default null */ - gradientTransform: TMat2D | null = null; + declare gradientTransform: TMat2D | null; /** * coordinates units for coords. @@ -66,35 +66,35 @@ export class Gradient< * @type GradientUnits * @default 'pixels' */ - gradientUnits: GradientUnits; + declare gradientUnits: GradientUnits; /** * Gradient type linear or radial * @type GradientType * @default 'linear' */ - type: T; + declare type: T; /** * Defines how the gradient is located in space and spread * @type GradientCoords */ - coords: GradientCoords; + declare coords: GradientCoords; /** * Defines how many colors a gradient has and how they are located on the axis * defined by coords * @type GradientCoords */ - colorStops: ColorStop[]; + declare colorStops: ColorStop[]; /** * If true, this object will not be exported during the serialization of a canvas * @type boolean */ - excludeFromExport?: boolean; + declare excludeFromExport?: boolean; - private id: string | number; + private declare id: string | number; constructor({ type = 'linear' as T, @@ -103,13 +103,13 @@ export class Gradient< colorStops = [], offsetX = 0, offsetY = 0, - gradientTransform, + gradientTransform = null, id, }: GradientOptions) { this.id = id ? `${id}_${uid()}` : uid(); this.type = type; this.gradientUnits = gradientUnits; - this.gradientTransform = gradientTransform || null; + this.gradientTransform = gradientTransform; this.offsetX = offsetX; this.offsetY = offsetY; this.coords = { diff --git a/src/intersection.class.ts b/src/intersection.class.ts index e23e1d406bc..ab7e5249d82 100644 --- a/src/intersection.class.ts +++ b/src/intersection.class.ts @@ -22,9 +22,9 @@ const isContainedInInterval = (T: Point, A: Point, B: Point) => { }; export class Intersection { - points: Point[]; + declare points: Point[]; - status?: IntersectionType; + declare status?: IntersectionType; constructor(status?: IntersectionType) { this.status = status; diff --git a/src/mixins/collection.mixin.ts b/src/mixins/collection.mixin.ts index 40f31e632d5..733721c4c1d 100644 --- a/src/mixins/collection.mixin.ts +++ b/src/mixins/collection.mixin.ts @@ -6,6 +6,7 @@ export function createCollectionMixin(Base: TBase) { class Collection extends Base { /** * @type {FabricObject[]} + * @TODO needs to end up in the constructor too */ _objects: FabricObject[] = []; diff --git a/src/mixins/itext_behavior.mixin.ts b/src/mixins/itext_behavior.mixin.ts index b0caeb4d91d..328f188eaba 100644 --- a/src/mixins/itext_behavior.mixin.ts +++ b/src/mixins/itext_behavior.mixin.ts @@ -18,46 +18,49 @@ const reNonWord = /[ \n\.,;!\?\-]/; export abstract class ITextBehaviorMixin< EventSpec extends ObjectEvents > extends Text { - abstract isEditing: boolean; - abstract cursorDelay: number; - abstract selectionStart: number; - abstract selectionEnd: number; - abstract cursorDuration: number; - abstract editable: boolean; - abstract editingBorderColor: string; + declare abstract isEditing: boolean; + declare abstract cursorDelay: number; + declare abstract selectionStart: number; + declare abstract selectionEnd: number; + declare abstract cursorDuration: number; + declare abstract editable: boolean; + declare abstract editingBorderColor: string; - abstract compositionStart: number; - abstract compositionEnd: number; + declare abstract compositionStart: number; + declare abstract compositionEnd: number; - abstract hiddenTextarea: HTMLTextAreaElement; + declare abstract hiddenTextarea: HTMLTextAreaElement; /** * Helps determining when the text is in composition, so that the cursor * rendering is altered. */ - protected inCompositionMode: boolean; - - protected _reSpace: RegExp; - private _currentTickState: { isAborted: boolean; abort: () => void }; - private _cursorTimeout1: number; - private _cursorTimeout2: number; - private _currentTickCompleteState: { isAborted: boolean; abort: () => void }; - protected _currentCursorOpacity: number; - private _textBeforeEdit: string; - protected __isMousedown: boolean; - protected __selectionStartOnMouseDown: number; - private __dragImageDisposer: VoidFunction; - private __dragStartFired: boolean; - protected __isDragging: boolean; - protected __dragStartSelection: { + protected declare inCompositionMode: boolean; + + protected declare _reSpace: RegExp; + private declare _currentTickState: { isAborted: boolean; abort: () => void }; + private declare _cursorTimeout1: number; + private declare _cursorTimeout2: number; + private declare _currentTickCompleteState: { + isAborted: boolean; + abort: () => void; + }; + protected declare _currentCursorOpacity: number; + private declare _textBeforeEdit: string; + protected declare __isMousedown: boolean; + protected declare __selectionStartOnMouseDown: number; + private declare __dragImageDisposer: VoidFunction; + private declare __dragStartFired: boolean; + protected declare __isDragging: boolean; + protected declare __dragStartSelection: { selectionStart: number; selectionEnd: number; }; - protected __isDraggingOver: boolean; - protected selected: boolean; - protected __lastSelected: boolean; - protected cursorOffsetCache: { left?: number; top?: number } = {}; - protected _savedProps: { + protected declare __isDraggingOver: boolean; + protected declare selected: boolean; + protected declare __lastSelected: boolean; + protected declare cursorOffsetCache: { left?: number; top?: number }; + protected declare _savedProps: { hasControls: boolean; borderColor: string; lockMovementX: boolean; @@ -67,7 +70,7 @@ export abstract class ITextBehaviorMixin< defaultCursor: string; moveCursor: CSSStyleDeclaration['cursor']; }; - protected _selectionDirection: 'left' | 'right' | null; + protected declare _selectionDirection: 'left' | 'right' | null; abstract initHiddenTextarea(): void; abstract initCursorSelectionHandlers(): void; diff --git a/src/mixins/itext_click_behavior.mixin.ts b/src/mixins/itext_click_behavior.mixin.ts index 6cc5188304f..c23ef4cae5c 100644 --- a/src/mixins/itext_click_behavior.mixin.ts +++ b/src/mixins/itext_click_behavior.mixin.ts @@ -9,10 +9,10 @@ import { ITextKeyBehaviorMixin } from './itext_key_behavior.mixin'; export abstract class ITextClickBehaviorMixin< EventSpec extends ObjectEvents > extends ITextKeyBehaviorMixin { - private __lastClickTime: number; - private __lastLastClickTime: number; - private __lastPointer: IPoint | Record; - private __newClickTime: number; + private declare __lastClickTime: number; + private declare __lastLastClickTime: number; + private declare __lastPointer: IPoint | Record; + private declare __newClickTime: number; /** * Initializes "dbclick" event handler diff --git a/src/mixins/itext_key_behavior.mixin.ts b/src/mixins/itext_key_behavior.mixin.ts index 1b6af83e897..feff5723e59 100644 --- a/src/mixins/itext_key_behavior.mixin.ts +++ b/src/mixins/itext_key_behavior.mixin.ts @@ -22,21 +22,21 @@ export abstract class ITextKeyBehaviorMixin< * this.keysMap = Object.assign({}, this.keysMap); * The function must be in IText.prototype.myFunction And will receive event as args[0] */ - keysMap: TKeyMapIText; + declare keysMap: TKeyMapIText; - keysMapRtl: TKeyMapIText; + declare keysMapRtl: TKeyMapIText; /** * For functionalities on keyUp + ctrl || cmd */ - ctrlKeysMapUp: TKeyMapIText; + declare ctrlKeysMapUp: TKeyMapIText; /** * For functionalities on keyDown + ctrl || cmd */ - ctrlKeysMapDown: TKeyMapIText; + declare ctrlKeysMapDown: TKeyMapIText; - hiddenTextarea: HTMLTextAreaElement | null; + declare hiddenTextarea: HTMLTextAreaElement | null; /** * DOM container to append the hiddenTextarea. @@ -46,11 +46,11 @@ export abstract class ITextKeyBehaviorMixin< * @type HTMLElement * @default */ - hiddenTextareaContainer?: HTMLElement | null; + declare hiddenTextareaContainer?: HTMLElement | null; - private _clickHandlerInitialized: boolean; - private _copyDone: boolean; - private fromPaste: boolean; + private declare _clickHandlerInitialized: boolean; + private declare _copyDone: boolean; + private declare fromPaste: boolean; /** * Initializes hidden textarea (needed to bring up keyboard in iOS) diff --git a/src/mixins/object.svg_export.ts b/src/mixins/object.svg_export.ts index d50ebf27423..e54a9d1793e 100644 --- a/src/mixins/object.svg_export.ts +++ b/src/mixins/object.svg_export.ts @@ -9,7 +9,7 @@ export class FabricObjectSVGExportMixin { * This reference is a UID in the fabric namespace and is temporary stored here. * @type {String} */ - clipPathId?: string; + declare clipPathId?: string; /** * Returns styles-string for svg-export diff --git a/src/mixins/shared_methods.mixin.ts b/src/mixins/shared_methods.mixin.ts index 30c9ad10a39..26d14c85b86 100644 --- a/src/mixins/shared_methods.mixin.ts +++ b/src/mixins/shared_methods.mixin.ts @@ -3,10 +3,11 @@ import { Observable } from './observable.mixin'; export class CommonMethods extends Observable { /** - * Sets object's properties from options + * Sets object's properties from options, for initialization only + * @protected * @param {Object} [options] Options object */ - _setOptions(options: any) { + protected _setOptions(options: any = {}) { for (const prop in options) { this.set(prop, options[prop]); } diff --git a/src/mixins/text_style.mixin.ts b/src/mixins/text_style.mixin.ts index 403fadd2a6e..5f00ec59c34 100644 --- a/src/mixins/text_style.mixin.ts +++ b/src/mixins/text_style.mixin.ts @@ -10,10 +10,10 @@ export type TextStyle = { export abstract class TextStyleMixin< EventSpec extends ObjectEvents > extends FabricObject { - abstract styles: TextStyle; - protected abstract _textLines: string[][]; - protected abstract _forceClearCache: boolean; - protected abstract _styleProperties: string[]; + declare abstract styles: TextStyle; + protected declare abstract _textLines: string[][]; + protected declare abstract _forceClearCache: boolean; + protected declare abstract _styleProperties: string[]; abstract get2DCursorLocation( selectionStart: number, skipWrapping?: boolean diff --git a/src/parser/elements_parser.ts b/src/parser/elements_parser.ts index 9a1b5b83261..a02c2136bf2 100644 --- a/src/parser/elements_parser.ts +++ b/src/parser/elements_parser.ts @@ -9,6 +9,7 @@ import { qrDecompose, } from '../util/misc/matrix'; import { storage } from './constants'; +import { removeTransformMatrixForSvgParsing } from '../util/transform_matrix_removal'; const ElementsParser = function ( elements, @@ -69,7 +70,7 @@ const ElementsParser = function ( if (obj instanceof Image && obj._originalElement) { _options = obj.parsePreserveAspectRatioAttribute(el); } - obj._removeTransformMatrix(_options); + removeTransformMatrixForSvgParsing(obj, _options); this.resolveClipPath(obj, el); this.reviver && this.reviver(el, obj); this.instances[index] = obj; @@ -108,7 +109,7 @@ const ElementsParser = function ( proto.createClipPathCallback = function (obj, container) { return function (_newObj) { - _newObj._removeTransformMatrix(); + removeTransformMatrixForSvgParsing(_newObj); _newObj.fillRule = _newObj.clipRule; container.push(_newObj); }; diff --git a/src/pattern.class.ts b/src/pattern.class.ts index 4828805f9ee..177a91701d4 100644 --- a/src/pattern.class.ts +++ b/src/pattern.class.ts @@ -78,15 +78,15 @@ export class Pattern { /** * The actual pixel source of the pattern */ - source!: CanvasImageSource; + declare source!: CanvasImageSource; /** * If true, this object will not be exported during the serialization of a canvas * @type boolean */ - excludeFromExport?: boolean; + declare excludeFromExport?: boolean; - readonly id: number; + declare readonly id: number; /** * Constructor diff --git a/src/point.class.ts b/src/point.class.ts index 5cd3c0c1f37..f444f39cc66 100644 --- a/src/point.class.ts +++ b/src/point.class.ts @@ -11,9 +11,9 @@ export interface IPoint { * Adaptation of work of Kevin Lindsey(kevin@kevlindev.com) */ export class Point implements IPoint { - x: number; + declare x: number; - y: number; + declare y: number; constructor(); constructor(x: number, y: number); diff --git a/src/shadow.class.ts b/src/shadow.class.ts index 8d94d8bea29..fc77c24b798 100644 --- a/src/shadow.class.ts +++ b/src/shadow.class.ts @@ -14,41 +14,41 @@ export class Shadow { * @type String * @default */ - color: string; + declare color: string; /** * Shadow blur * @type Number */ - blur: number; + declare blur: number; /** * Shadow horizontal offset * @type Number * @default */ - offsetX: number; + declare offsetX: number; /** * Shadow vertical offset * @type Number * @default */ - offsetY: number; + declare offsetY: number; /** * Whether the shadow should affect stroke operations * @type Boolean * @default */ - affectStroke: boolean; + declare affectStroke: boolean; /** * Indicates whether toObject should include default values * @type Boolean * @default */ - includeDefaultValues: boolean; + declare includeDefaultValues: boolean; /** * When `false`, the shadow will scale with the object. @@ -57,9 +57,9 @@ export class Shadow { * @type Boolean * @default */ - nonScaling: boolean; + declare nonScaling: boolean; - id: number; + declare id: number; /** * @see {@link http://fabricjs.com/shadows|Shadow demo} diff --git a/src/shapes/Object/AnimatableObject.ts b/src/shapes/Object/AnimatableObject.ts index 6daacfd9094..9d8d5f44eae 100644 --- a/src/shapes/Object/AnimatableObject.ts +++ b/src/shapes/Object/AnimatableObject.ts @@ -21,7 +21,7 @@ export abstract class AnimatableObject< * List of properties to consider for animating colors. * @type String[] */ - colorProperties: string[]; + declare colorProperties: string[]; /** * Animates object's properties diff --git a/src/shapes/Object/FabricObject.ts b/src/shapes/Object/FabricObject.ts index 39af92dae70..765ce935063 100644 --- a/src/shapes/Object/FabricObject.ts +++ b/src/shapes/Object/FabricObject.ts @@ -16,4 +16,4 @@ export class FabricObject< applyMixins(FabricObject, [FabricObjectSVGExportMixin, StatefulMixin]); -export { fabricObjectDefaultValues } from './Object'; +export { cacheProperties, stateProperties } from './defaultValues'; diff --git a/src/shapes/Object/InteractiveObject.ts b/src/shapes/Object/InteractiveObject.ts index 6851bf5edb5..63c58fe71f5 100644 --- a/src/shapes/Object/InteractiveObject.ts +++ b/src/shapes/Object/InteractiveObject.ts @@ -8,7 +8,6 @@ import { qrDecompose, TQrDecomposeOut, } from '../../util/misc/matrix'; -import { ObjectGeometry } from './ObjectGeometry'; import type { Control } from '../../controls/control.class'; import { sizeAfterTransform } from '../../util/misc/objectTransforms'; import { ObjectEvents, TPointerEvent } from '../../EventTypeDefs'; @@ -37,7 +36,18 @@ export class InteractiveFabricObject< * The coordinates depends from the controls positionHandler and are used * to draw and locate controls */ - oCoords: Record = {}; + declare oCoords: Record; + + /** + * When `true`, cache does not get updated during scaling. The picture will get blocky if scaled + * too much and will be redrawn with correct details at the end of scaling. + * this setting is performance and application dependant. + * default to true + * since 1.7.0 + * @type Boolean + * @default true + */ + declare noScaleCache: boolean; /** * keeps the value of the last hovered corner during mouse move. @@ -48,39 +58,39 @@ export class InteractiveFabricObject< * @type number|string|any * @default 0 */ - __corner: 0 | string; + declare __corner: 0 | string; /** * a map of control visibility for this object. * this was left when controls were introduced to do not brea the api too much * this takes priority over the generic control visibility */ - _controlsVisibility: Record; + declare _controlsVisibility: Record; /** * The angle that an object will lock to while rotating. * @type [TDegree] */ - snapAngle?: TDegree; + declare snapAngle?: TDegree; /** * The angle difference from the current snapped angle in which snapping should occur. * When undefined, the snapThreshold will default to the snapAngle. * @type [TDegree] */ - snapThreshold?: TDegree; + declare snapThreshold?: TDegree; /** * holds the controls for the object. * controls are added by default_controls.js */ - controls: TControlSet; + declare controls: TControlSet; /** * internal boolean to signal the code that the object is * part of the move action. */ - isMoving?: boolean; + declare isMoving?: boolean; /** * internal boolean to signal the code that the object is @@ -89,7 +99,7 @@ export class InteractiveFabricObject< * they need to be either both private or more generic * Canvas class needs to see this variable */ - __isDragging?: boolean; + declare __isDragging?: boolean; /** * A boolean used from the gesture module to keep tracking of a scaling @@ -99,7 +109,7 @@ export class InteractiveFabricObject< * @TODO use git blame to investigate why it was added * DON'T USE IT. WE WILL TRY TO REMOVE IT */ - _scaling?: boolean; + declare _scaling?: boolean; declare canvas?: Canvas; @@ -111,6 +121,27 @@ export class InteractiveFabricObject< super(options); } + /** + * Update width and height of the canvas for cache + * returns true or false if canvas needed resize. + * @private + * @return {Boolean} true if the canvas has been resized + */ + _updateCacheCanvas() { + const targetCanvas = this.canvas; + if (this.noScaleCache && targetCanvas && targetCanvas._currentTransform) { + const target = targetCanvas._currentTransform.target, + action = targetCanvas._currentTransform.action; + if ( + this === (target as InteractiveFabricObject) && + action.startsWith('scale') + ) { + return false; + } + } + return super._updateCacheCanvas(); + } + /** * Determines which corner has been clicked * @private @@ -221,11 +252,7 @@ export class InteractiveFabricObject< * @return {void} */ setCoords(): void { - if (this.callSuper) { - ObjectGeometry.prototype.setCoords.call(this); - } else { - super.setCoords(); - } + super.setCoords(); // set coordinates of the draggable boxes in the corners used to scale/rotate the image this.oCoords = this.calcOCoords(); this._setCornerCoords(); diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts index d40446196bc..4614bf60af5 100644 --- a/src/shapes/Object/Object.ts +++ b/src/shapes/Object/Object.ts @@ -1,5 +1,3 @@ -// @ts-nocheck -import { getEnv } from '../../env'; import { cache } from '../../cache'; import { config } from '../../config'; import { ALIASING_LIMIT, iMatrix, VERSION } from '../../constants'; @@ -20,11 +18,7 @@ import { clone } from '../../util/lang_object'; import { capitalize } from '../../util/lang_string'; import { capValue } from '../../util/misc/capValue'; import { createCanvasElement, toDataURL } from '../../util/misc/dom'; -import { - invertTransform, - qrDecompose, - transformPoint, -} from '../../util/misc/matrix'; +import { invertTransform, qrDecompose } from '../../util/misc/matrix'; import { enlivenObjectEnlivables } from '../../util/misc/objectEnlive'; import { resetObjectTransform, @@ -34,8 +28,17 @@ import { pick } from '../../util/misc/pick'; import { toFixed } from '../../util/misc/toFixed'; import type { Group } from '../group.class'; import { StaticCanvas } from '../../canvas/static_canvas.class'; -import { isTextObject } from '../../util/types'; +import { isFiller, isSerializableFiller, isTextObject } from '../../util/types'; import { Image } from '../image.class'; +import { + cacheProperties, + fabricObjectDefaultValues, + stateProperties, +} from './defaultValues'; +import type { Gradient } from '../../gradient/gradient.class'; +import type { Pattern } from '../../pattern.class'; +import type { Canvas } from '../../canvas/canvas_events'; +import { removeTransformMatrixForSvgParsing } from '../../util/transform_matrix_removal'; export type TCachedFabricObject = FabricObject & Required< @@ -52,9 +55,6 @@ export type TCachedFabricObject = FabricObject & _cacheContext: CanvasRenderingContext2D; }; -// temporary hack for unfinished migration -type TCallSuper = (arg0: string, ...moreArgs: any[]) => any; - /** * Root object class from which all 2d shape classes inherit from * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#objects} @@ -86,56 +86,56 @@ type TCallSuper = (arg0: string, ...moreArgs: any[]) => any; export class FabricObject< EventSpec extends ObjectEvents = ObjectEvents > extends AnimatableObject { - type: string; + declare type: string; /** * Opacity of an object * @type Number * @default 1 */ - opacity: number; + declare opacity: number; /** * Size of object's controlling corners (in pixels) * @type Number * @default 13 */ - cornerSize: number; + declare cornerSize: number; /** * Size of object's controlling corners when touch interaction is detected * @type Number * @default 24 */ - touchCornerSize: number; + declare touchCornerSize: number; /** * When true, object's controlling corners are rendered as transparent inside (i.e. stroke instead of fill) * @type Boolean * @default true */ - transparentCorners: boolean; + declare transparentCorners: boolean; /** * Default cursor value used when hovering over this object on canvas * @type CSSStyleDeclaration['cursor'] | null * @default null */ - hoverCursor: CSSStyleDeclaration['cursor'] | null; + declare hoverCursor: CSSStyleDeclaration['cursor'] | null; /** * Default cursor value used when moving this object on canvas * @type CSSStyleDeclaration['cursor'] | null * @default null */ - moveCursor: CSSStyleDeclaration['cursor'] | null; + declare moveCursor: CSSStyleDeclaration['cursor'] | null; /** * Color of controlling borders of an object (when it's active) * @type String * @default rgb(178,204,255) */ - borderColor: string; + declare borderColor: string; /** * Array specifying dash pattern of an object's borders (hasBorder must be true) @@ -143,14 +143,14 @@ export class FabricObject< * @type Array | null * default null; */ - borderDashArray: number[] | null; + declare borderDashArray: number[] | null; /** * Color of controlling corners of an object (when it's active) * @type String * @default rgb(178,204,255) */ - cornerColor: string; + declare cornerColor: string; /** * Color of controlling corners of an object (when it's active and transparentCorners false) @@ -158,22 +158,25 @@ export class FabricObject< * @type String * @default null */ - cornerStrokeColor: string; + declare cornerStrokeColor: string; /** * Specify style of control, 'rect' or 'circle' + * This is deprecated. In the futuer there will be a standard control render + * And you can swap it with one of the alternative proposed with the control api * @since 1.6.2 * @type 'rect' | 'circle' * @default rect + * @deprecated */ - cornerStyle: 'rect' | 'circle'; + declare cornerStyle: 'rect' | 'circle'; /** * Array specifying dash pattern of an object's control (hasBorder must be true) * @since 1.6.2 * @type Array | null */ - cornerDashArray: number[] | null; + declare cornerDashArray: number[] | null; /** * When true, this object will use center point as the origin of transformation @@ -183,7 +186,7 @@ export class FabricObject< * @type Boolean * @default */ - centeredScaling: false; + declare centeredScaling: false; /** * When true, this object will use center point as the origin of transformation @@ -193,7 +196,7 @@ export class FabricObject< * @type Boolean * @default */ - centeredRotation: true; + declare centeredRotation: true; /** * When defined, an object is rendered via stroke and this property specifies its color @@ -201,7 +204,7 @@ export class FabricObject< * @type String * @default null */ - stroke: string | TFiller | null; + declare stroke: string | TFiller | null; /** * Color of object's fill @@ -209,7 +212,7 @@ export class FabricObject< * @type String * @default rgb(0,0,0) */ - fill: string | null; + declare fill: string | TFiller | null; /** * Fill rule used to fill an object @@ -218,14 +221,14 @@ export class FabricObject< * @type String * @default nonzero */ - fillRule: CanvasFillRule; + declare fillRule: CanvasFillRule; /** * Composite rule used for canvas globalCompositeOperation * @type String * @default */ - globalCompositeOperation: GlobalCompositeOperation; + declare globalCompositeOperation: GlobalCompositeOperation; /** * Background color of an object. @@ -233,64 +236,65 @@ export class FabricObject< * @type String * @default */ - backgroundColor: string; + declare backgroundColor: string; /** * Selection Background color of an object. colored layer behind the object when it is active. * does not mix good with globalCompositeOperation methods. * @type String + * @deprecated * @default */ - selectionBackgroundColor: string; + declare selectionBackgroundColor: string; /** * Array specifying dash pattern of an object's stroke (stroke must be defined) * @type Array * @default null; */ - strokeDashArray: number[] | null; + declare strokeDashArray: number[] | null; /** * Line offset of an object's stroke * @type Number * @default 0 */ - strokeDashOffset: number; + declare strokeDashOffset: number; /** * Line endings style of an object's stroke (one of "butt", "round", "square") * @type String * @default butt */ - strokeLineCap: CanvasLineCap; + declare strokeLineCap: CanvasLineCap; /** * Corner style of an object's stroke (one of "bevel", "round", "miter") * @type String * @default */ - strokeLineJoin: CanvasLineJoin; + declare strokeLineJoin: CanvasLineJoin; /** * Maximum miter length (used for strokeLineJoin = "miter") of an object's stroke * @type Number * @default 4 */ - strokeMiterLimit: number; + declare strokeMiterLimit: number; /** * Shadow object representing shadow of this shape * @type Shadow * @default null */ - shadow: Shadow | null; + declare shadow: Shadow | null; /** * Opacity of object's controlling borders when object is active and moving * @type Number * @default 0.4 */ - borderOpacityWhenMoving: number; + declare borderOpacityWhenMoving: number; /** * Scale factor of object's controlling borders @@ -300,14 +304,14 @@ export class FabricObject< * @type Number * @default 1 */ - borderScaleFactor: number; + declare borderScaleFactor: number; /** * Minimum allowed scale value of an object * @type Number * @default 0 */ - minScaleLimit: number; + declare minScaleLimit: number; /** * When set to `false`, an object can not be selected for modification (using either point-click-based or group-based selection). @@ -315,105 +319,105 @@ export class FabricObject< * @type Boolean * @default */ - selectable: boolean; + declare selectable: boolean; /** * When set to `false`, an object can not be a target of events. All events propagate through it. Introduced in v1.3.4 * @type Boolean * @default */ - evented: boolean; + declare evented: boolean; /** * When set to `false`, an object is not rendered on canvas * @type Boolean * @default */ - visible: boolean; + declare visible: boolean; /** * When set to `false`, object's controls are not displayed and can not be used to manipulate object * @type Boolean * @default */ - hasControls: boolean; + declare hasControls: boolean; /** * When set to `false`, object's controlling borders are not rendered * @type Boolean * @default */ - hasBorders: boolean; + declare hasBorders: boolean; /** * When set to `true`, objects are "found" on canvas on per-pixel basis rather than according to bounding box * @type Boolean * @default */ - perPixelTargetFind: boolean; + declare perPixelTargetFind: boolean; /** * When `false`, default object's values are not included in its serialization * @type Boolean * @default */ - includeDefaultValues: boolean; + declare includeDefaultValues: boolean; /** * When `true`, object horizontal movement is locked * @type Boolean * @default */ - lockMovementX: boolean; + declare lockMovementX: boolean; /** * When `true`, object vertical movement is locked * @type Boolean * @default */ - lockMovementY: boolean; + declare lockMovementY: boolean; /** * When `true`, object rotation is locked * @type Boolean * @default */ - lockRotation: boolean; + declare lockRotation: boolean; /** * When `true`, object horizontal scaling is locked * @type Boolean * @default */ - lockScalingX: boolean; + declare lockScalingX: boolean; /** * When `true`, object vertical scaling is locked * @type Boolean * @default */ - lockScalingY: boolean; + declare lockScalingY: boolean; /** * When `true`, object horizontal skewing is locked * @type Boolean * @default */ - lockSkewingX: boolean; + declare lockSkewingX: boolean; /** * When `true`, object vertical skewing is locked * @type Boolean * @default */ - lockSkewingY: boolean; + declare lockSkewingY: boolean; /** * When `true`, object cannot be flipped by scaling into negative values * @type Boolean * @default */ - lockScalingFlip: boolean; + declare lockScalingFlip: boolean; /** * When `true`, object is not exported in OBJECT/JSON @@ -421,7 +425,7 @@ export class FabricObject< * @type Boolean * @default */ - excludeFromExport: boolean; + declare excludeFromExport: boolean; /** * When `true`, object is cached on an additional canvas. @@ -431,18 +435,7 @@ export class FabricObject< * @type Boolean * @default true */ - objectCaching: boolean; - - /** - * When `true`, cache does not get updated during scaling. The picture will get blocky if scaled - * too much and will be redrawn with correct details at the end of scaling. - * this setting is performance and application dependant. - * default to true - * since 1.7.0 - * @type Boolean - * @default true - */ - noScaleCache: boolean; + declare objectCaching: boolean; /** * When set to `true`, object's cache will be rerendered next render call. @@ -450,14 +443,14 @@ export class FabricObject< * @type Boolean * @default true */ - dirty: boolean; + declare dirty: boolean; /** * Determines if the fill or the stroke is drawn first (one of "fill" or "stroke") * @type String * @default */ - paintFirst: 'fill' | 'stroke'; + declare paintFirst: 'fill' | 'stroke'; /** * When 'down', object is set to active on mousedown/touchstart @@ -468,15 +461,15 @@ export class FabricObject< * @type String * @default 'down' */ - activeOn: 'down' | 'up'; + declare activeOn: 'down' | 'up'; /** - * List of properties to consider when checking if state - * of an object is changed (hasStateChanged) - * as well as for history (undo/redo) purposes + * This list of properties is used to check if the state of an object is changed. + * This state change now is only used for children of groups to understand if a group + * needs its cache regenerated during a .set call * @type Array */ - stateProperties: string[]; + declare stateProperties: string[]; /** * List of properties to consider when checking if cache needs refresh @@ -485,7 +478,7 @@ export class FabricObject< * and refreshed at the next render * @type Array */ - cacheProperties: string[]; + declare cacheProperties: string[]; /** * a fabricObject that, without stroke define a clipping area with their shape. filled in black @@ -494,7 +487,7 @@ export class FabricObject< * If you want 0,0 of a clipPath to align with an object center, use clipPath.originX/Y to 'center' * @type FabricObject */ - clipPath?: FabricObject; + declare clipPath?: FabricObject; /** * Meaningful ONLY when the object is used as clipPath. @@ -503,7 +496,7 @@ export class FabricObject< * @type boolean * @default false */ - inverted: boolean; + declare inverted: boolean; /** * Meaningful ONLY when the object is used as clipPath. @@ -515,7 +508,7 @@ export class FabricObject< * @type boolean * @default false */ - absolutePositioned: boolean; + declare absolutePositioned: boolean; /** * Quick access for the _cacheCanvas rendering context @@ -536,7 +529,7 @@ export class FabricObject< * @default undefined * @private */ - _cacheCanvas?: HTMLCanvasElement; + declare _cacheCanvas?: HTMLCanvasElement; /** * Size of the cache canvas, width @@ -545,7 +538,7 @@ export class FabricObject< * @default undefined * @private */ - cacheWidth?: number; + declare cacheWidth?: number; /** * Size of the cache canvas, height @@ -554,7 +547,7 @@ export class FabricObject< * @default undefined * @private */ - cacheHeight?: number; + declare cacheHeight?: number; /** * zoom level used on the cacheCanvas to draw the cache, X axe @@ -563,7 +556,7 @@ export class FabricObject< * @default undefined * @private */ - zoomX?: number; + declare zoomX?: number; /** * zoom level used on the cacheCanvas to draw the cache, Y axe @@ -572,7 +565,7 @@ export class FabricObject< * @default undefined * @private */ - zoomY?: number; + declare zoomY?: number; /** * zoom level used on the cacheCanvas to draw the cache, Y axe @@ -581,7 +574,7 @@ export class FabricObject< * @default undefined * @private */ - cacheTranslationX?: number; + declare cacheTranslationX?: number; /** * translation of the cacheCanvas away from the center, for subpixel accuracy and crispness @@ -590,7 +583,7 @@ export class FabricObject< * @default undefined * @private */ - cacheTranslationY?: number; + declare cacheTranslationY?: number; /** * A reference to the parent of the object, usually a Group @@ -598,13 +591,13 @@ export class FabricObject< * @default undefined * @private */ - group?: Group; + declare group?: Group; /** * A reference to the parent of the object * Used to keep the original parent ref when the object has been added to an ActiveSelection, hence loosing the `group` ref */ - __owningGroup?: Group; + declare __owningGroup?: Group; /** * Indicate if the object is sitting on a cache dedicated to it @@ -613,7 +606,7 @@ export class FabricObject< * @default undefined * @private */ - ownCaching?: boolean; + declare ownCaching?: boolean; /** * Private. indicates if the object inside a group is on a transformed context or not @@ -622,9 +615,7 @@ export class FabricObject< * @default undefined * @private */ - _transformDone?: boolean; - - callSuper?: TCallSuper; + declare _transformDone?: boolean; /** * Constructor @@ -632,9 +623,7 @@ export class FabricObject< */ constructor(options?: Partial>) { super(); - if (options) { - this.setOptions(options); - } + this.setOptions(options); } /** @@ -738,14 +727,6 @@ export class FabricObject< * @return {Boolean} true if the canvas has been resized */ _updateCacheCanvas() { - const targetCanvas = this.canvas; - if (this.noScaleCache && targetCanvas && targetCanvas._currentTransform) { - const target = targetCanvas._currentTransform.target, - action = targetCanvas._currentTransform.action; - if (this === target && action.slice && action.slice(0, 5) === 'scale') { - return false; - } - } const canvas = this._cacheCanvas, context = this._cacheContext, dims = this._limitCacheSize(this._getCacheCanvasDimensions()), @@ -791,8 +772,8 @@ export class FabricObject< shouldRedraw = true; shouldResizeCanvas = true; // IMHO in those lines we are using zoomX and zoomY not the this version. - additionalWidth += this.getHeightOfLine(0) * this.zoomX; - additionalHeight += this.getHeightOfLine(0) * this.zoomY; + additionalWidth += this.getHeightOfLine(0) * this.zoomX!; + additionalHeight += this.getHeightOfLine(0) * this.zoomY!; } if (shouldRedraw) { if (shouldResizeCanvas) { @@ -820,10 +801,12 @@ export class FabricObject< } /** - * Sets object's properties from options + * Sets object's properties from options, for class constructor only. + * Needs to be overridden for different defaults. + * @protected * @param {Object} [options] Options object */ - setOptions(options: Record = {}) { + protected setOptions(options: Record = {}) { this._setOptions(options); } @@ -834,7 +817,7 @@ export class FabricObject< transform(ctx: CanvasRenderingContext2D) { const needFullTransform = (this.group && !this.group._transformDone) || - (this.group && this.canvas && ctx === this.canvas.contextTop); + (this.group && this.canvas && ctx === (this.canvas as Canvas).contextTop); const m = this.calcTransformMatrix(!needFullTransform); ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]); } @@ -844,7 +827,7 @@ export class FabricObject< * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output * @return {Object} Object representation of an instance */ - toObject(propertiesToInclude?: (keyof this | string)[]): Record { + toObject(propertiesToInclude?: string[]): Record { const NUM_FRACTION_DIGITS = config.NUM_FRACTION_DIGITS, clipPathData = this.clipPath && !this.clipPath.excludeFromExport @@ -855,7 +838,7 @@ export class FabricObject< } : null, object = { - ...pick(this, propertiesToInclude), + ...pick(this, propertiesToInclude as (keyof this)[]), type: this.type, version: VERSION, originX: this.originX, @@ -864,12 +847,12 @@ export class FabricObject< top: toFixed(this.top, NUM_FRACTION_DIGITS), width: toFixed(this.width, NUM_FRACTION_DIGITS), height: toFixed(this.height, NUM_FRACTION_DIGITS), - fill: - this.fill && this.fill.toObject ? this.fill.toObject() : this.fill, - stroke: - this.stroke && this.stroke.toObject - ? this.stroke.toObject() - : this.stroke, + fill: isSerializableFiller(this.fill) + ? this.fill.toObject() + : this.fill, + stroke: isSerializableFiller(this.stroke) + ? this.stroke.toObject() + : this.stroke, strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS), strokeDashArray: this.strokeDashArray ? this.strokeDashArray.concat() @@ -909,7 +892,7 @@ export class FabricObject< * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output * @return {Object} Object representation of an instance */ - toDatalessObject(propertiesToInclude?: (keyof this | string)[]) { + toDatalessObject(propertiesToInclude?: string[]) { // will be overwritten by subclasses return this.toObject(propertiesToInclude); } @@ -1018,7 +1001,7 @@ export class FabricObject< * @param {*} value */ _set(key: string, value: any) { - const isChanged = this[key] !== value; + const isChanged = this[key as keyof this] !== value; if (key === 'scaleX' || key === 'scaleY') { value = this._constrainScale(value); @@ -1029,21 +1012,22 @@ export class FabricObject< } else if (key === 'scaleY' && value < 0) { this.flipY = !this.flipY; value *= -1; + // i don't like this automatic initialization here } else if (key === 'shadow' && value && !(value instanceof Shadow)) { value = new Shadow(value); } else if (key === 'dirty' && this.group) { this.group.set('dirty', value); } - this[key] = value; + this[key as keyof this] = value; if (isChanged) { const groupNeedsUpdate = this.group && this.group.isOnACache(); if (this.cacheProperties.includes(key)) { this.dirty = true; - groupNeedsUpdate && this.group.set('dirty', true); + groupNeedsUpdate && this.group!.set('dirty', true); } else if (groupNeedsUpdate && this.stateProperties.includes(key)) { - this.group.set('dirty', true); + this.group!.set('dirty', true); } } return this; @@ -1088,7 +1072,7 @@ export class FabricObject< this._setShadow(ctx); if (this.shouldCache()) { this.renderCache(); - this.drawCacheOnCanvas(ctx); + (this as TCachedFabricObject).drawCacheOnCanvas(ctx); } else { this._removeCacheCanvas(); this.dirty = false; @@ -1097,6 +1081,10 @@ export class FabricObject< ctx.restore(); } + drawSelectionBackground(ctx: CanvasRenderingContext2D) { + /* no op */ + } + renderCache(options?: any) { options = options || {}; if (!this._cacheCanvas || !this._cacheContext) { @@ -1204,7 +1192,10 @@ export class FabricObject< * @param {CanvasRenderingContext2D} ctx Context to render on * @param {FabricObject} clipPath */ - drawClipPathOnCache(ctx: CanvasRenderingContext2D, clipPath: FabricObject) { + drawClipPathOnCache( + ctx: CanvasRenderingContext2D, + clipPath: TCachedFabricObject + ) { ctx.save(); // DEBUG: uncomment this line, comment the following // ctx.globalAlpha = 0.4 @@ -1219,11 +1210,11 @@ export class FabricObject< ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]); } clipPath.transform(ctx); - ctx.scale(1 / clipPath.zoomX!, 1 / clipPath.zoomY!); + ctx.scale(1 / clipPath.zoomX, 1 / clipPath.zoomY); ctx.drawImage( - clipPath._cacheCanvas!, - -clipPath.cacheTranslationX!, - -clipPath.cacheTranslationY! + clipPath._cacheCanvas, + -clipPath.cacheTranslationX, + -clipPath.cacheTranslationY ); ctx.restore(); } @@ -1265,19 +1256,19 @@ export class FabricObject< clipPath.shouldCache(); clipPath._transformDone = true; clipPath.renderCache({ forClipping: true }); - this.drawClipPathOnCache(ctx, clipPath); + this.drawClipPathOnCache(ctx, clipPath as TCachedFabricObject); } /** * Paint the cached copy of the object on the target context. * @param {CanvasRenderingContext2D} ctx Context to render on */ - drawCacheOnCanvas(ctx: CanvasRenderingContext2D) { - ctx.scale(1 / this.zoomX!, 1 / this.zoomY!); + drawCacheOnCanvas(this: TCachedFabricObject, ctx: CanvasRenderingContext2D) { + ctx.scale(1 / this.zoomX, 1 / this.zoomY); ctx.drawImage( - this._cacheCanvas!, - -this.cacheTranslationX!, - -this.cacheTranslationY! + this._cacheCanvas, + -this.cacheTranslationX, + -this.cacheTranslationY ); } @@ -1360,11 +1351,11 @@ export class FabricObject< ctx.lineDashOffset = decl.strokeDashOffset; ctx.lineJoin = decl.strokeLineJoin; ctx.miterLimit = decl.strokeMiterLimit; - if (stroke.toLive) { + if (isFiller(stroke)) { if ( - stroke.gradientUnits === 'percentage' || - stroke.gradientTransform || - stroke.patternTransform + (stroke as Gradient<'linear'>).gradientUnits === 'percentage' || + (stroke as Gradient<'linear'>).gradientTransform || + (stroke as Pattern).patternTransform ) { // need to transform gradient in a pattern. // this is a slow process. If you are hitting this codepath, and the object @@ -1373,20 +1364,20 @@ export class FabricObject< this._applyPatternForTransformedGradient(ctx, stroke); } else { // is a simple gradient or pattern - ctx.strokeStyle = stroke.toLive(ctx, this); + ctx.strokeStyle = stroke.toLive(ctx); this._applyPatternGradientTransform(ctx, stroke); } } else { // is a color - ctx.strokeStyle = decl.stroke; + ctx.strokeStyle = decl.stroke as string; } } } _setFillStyles(ctx: CanvasRenderingContext2D, { fill }: Pick) { if (fill) { - if (fill.toLive) { - ctx.fillStyle = fill.toLive(ctx, this); + if (isFiller(fill)) { + ctx.fillStyle = fill.toLive(ctx); this._applyPatternGradientTransform(ctx, fill); } else { ctx.fillStyle = fill; @@ -1466,14 +1457,16 @@ export class FabricObject< ctx: CanvasRenderingContext2D, filler: TFiller ) { - if (!filler || !filler.toLive) { + if (!isFiller(filler)) { return { offsetX: 0, offsetY: 0 }; } - const t = filler.gradientTransform || filler.patternTransform; + const t = + (filler as Gradient<'linear'>).gradientTransform || + (filler as Pattern).patternTransform; const offsetX = -this.width / 2 + filler.offsetX || 0, offsetY = -this.height / 2 + filler.offsetY || 0; - if (filler.gradientUnits === 'percentage') { + if ((filler as Gradient<'linear'>).gradientUnits === 'percentage') { ctx.transform(this.width, 0, 0, this.height, offsetX, offsetY); } else { ctx.transform(1, 0, 0, 1, offsetX, offsetY); @@ -1575,6 +1568,9 @@ export class FabricObject< pCanvas.width = width; pCanvas.height = height; const pCtx = pCanvas.getContext('2d'); + if (!pCtx) { + return; + } pCtx.beginPath(); pCtx.moveTo(0, 0); pCtx.lineTo(width, 0); @@ -1597,64 +1593,17 @@ export class FabricObject< (retinaScaling * this.scaleX) / dims.zoomX, (retinaScaling * this.scaleY) / dims.zoomY ); - ctx.strokeStyle = pCtx.createPattern(pCanvas, 'no-repeat'); + ctx.strokeStyle = pCtx.createPattern(pCanvas, 'no-repeat') ?? ''; } /** * This function is an helper for svg import. it returns the center of the object in the svg * untransformed coordinates * @private - * @return {Object} center point from element coordinates + * @return {Point} center point from element coordinates */ _findCenterFromElement() { - return { x: this.left + this.width / 2, y: this.top + this.height / 2 }; - } - - /** - * This function is an helper for svg import. it decompose the transformMatrix - * and assign properties to object. - * untransformed coordinates - * @todo move away in the svg import stuff. - * @private - */ - _assignTransformMatrixProps() { - if (this.transformMatrix) { - const options = qrDecompose(this.transformMatrix); - this.flipX = false; - this.flipY = false; - this.set('scaleX', options.scaleX); - this.set('scaleY', options.scaleY); - this.angle = options.angle; - this.skewX = options.skewX; - this.skewY = 0; - } - } - - /** - * This function is an helper for svg import. it removes the transform matrix - * and set to object properties that fabricjs can handle - * @todo move away in the svg import stuff. - * @private - * @param {Object} preserveAspectRatioOptions - */ - _removeTransformMatrix(preserveAspectRatioOptions) { - let center = this._findCenterFromElement(); - if (this.transformMatrix) { - this._assignTransformMatrixProps(); - center = transformPoint(center, this.transformMatrix); - } - this.transformMatrix = null; - if (preserveAspectRatioOptions) { - this.scaleX *= preserveAspectRatioOptions.scaleX; - this.scaleY *= preserveAspectRatioOptions.scaleY; - this.cropX = preserveAspectRatioOptions.cropX; - this.cropY = preserveAspectRatioOptions.cropY; - center.x += preserveAspectRatioOptions.offsetLeft; - center.y += preserveAspectRatioOptions.offsetTop; - this.width = preserveAspectRatioOptions.width; - this.height = preserveAspectRatioOptions.height; - } - this.setPositionByOrigin(center, 'center', 'center'); + return new Point(this.left + this.width / 2, this.top + this.height / 2); } /** @@ -1662,9 +1611,11 @@ export class FabricObject< * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output * @returns {Promise} */ - clone(propertiesToInclude: (keyof this)[]) { + clone(propertiesToInclude: string[]) { const objectForm = this.toObject(propertiesToInclude); // todo ok understand this. is static or it isn't? + // TS is more an issue here than an helper. + // @ts-ignore return this.constructor.fromObject(objectForm); } @@ -1745,7 +1696,7 @@ export class FabricObject< // we need to make it so. el.width = Math.ceil(width); el.height = Math.ceil(height); - let canvas = new StaticCanvas(el, { + const canvas = new StaticCanvas(el, { enableRetinaScaling: false, renderOnAddRemove: false, skipOffscreen: false, @@ -1759,6 +1710,8 @@ export class FabricObject< 'center' ); const originalCanvas = this.canvas; + // static canvas and canvas have both an array of InteractiveObjects + // @ts-ignore this needs to be fixed somehow, or ignored globally canvas._objects = [this]; this.set('canvas', canvas); this.setCoords(); @@ -1776,8 +1729,6 @@ export class FabricObject< canvas._objects = []; // since render has settled it is safe to destroy canvas canvas.destroy(); - canvas = null; - return canvasEl; } @@ -1850,60 +1801,6 @@ export class FabricObject< } } - /** - * Centers object horizontally on canvas to which it was added last. - * You might need to call `setCoords` on an object after centering, to update controls area. - */ - centerH() { - this.canvas && this.canvas.centerObjectH(this); - return this; - } - - /** - * Centers object horizontally on current viewport of canvas to which it was added last. - * You might need to call `setCoords` on an object after centering, to update controls area. - */ - viewportCenterH() { - this.canvas && this.canvas.viewportCenterObjectH(this); - return this; - } - - /** - * Centers object vertically on canvas to which it was added last. - * You might need to call `setCoords` on an object after centering, to update controls area. - */ - centerV() { - this.canvas && this.canvas.centerObjectV(this); - return this; - } - - /** - * Centers object vertically on current viewport of canvas to which it was added last. - * You might need to call `setCoords` on an object after centering, to update controls area. - */ - viewportCenterV() { - this.canvas && this.canvas.viewportCenterObjectV(this); - return this; - } - - /** - * Centers object vertically and horizontally on canvas to which is was added last - * You might need to call `setCoords` on an object after centering, to update controls area. - */ - center() { - this.canvas && this.canvas.centerObject(this); - return this; - } - - /** - * Centers object on current viewport of canvas to which it was added last. - * You might need to call `setCoords` on an object after centering, to update controls area. - */ - viewportCenter() { - this.canvas && this.canvas.viewportCenterObject(this); - return this; - } - /** * This callback function is called by the parent group of an object every * time a non-delegated property changes on the group. It is passed the key @@ -1948,17 +1845,25 @@ export class FabricObject< */ static _fromObject( object: Record, - { extraParam, ...options }: { extraParam?: any; signal?: AbortSignal } = {} - ) { - return enlivenObjectEnlivables>( - clone(object, true), - options - ).then((enlivedMap) => { - // from the resulting enlived options, extract options.extraParam to arg0 - // to avoid accidental overrides later - const { [extraParam]: arg0, ...rest } = { ...options, ...enlivedMap }; - return extraParam ? new this(arg0, rest) : new this(rest); - }); + { + extraParam, + ...options + }: { extraParam?: string; signal?: AbortSignal } = {} + ): Promise { + return enlivenObjectEnlivables(clone(object, true), options).then( + (enlivedMap) => { + const allOptions = { ...options, ...enlivedMap }; + // from the resulting enlived options, extract options.extraParam to arg0 + // to avoid accidental overrides later + if (extraParam) { + const { [extraParam]: arg0, ...rest } = allOptions; + // @ts-ignore; + return new this(arg0, rest); + } else { + return new this(allOptions); + } + } + ); } /** @@ -1971,119 +1876,20 @@ export class FabricObject< static fromObject( object: Record, options?: { signal?: AbortSignal } - ) { + ): Promise { return this._fromObject(object, options); } } -export const fabricObjectDefaultValues = { - type: 'object', - originX: 'left', - originY: 'top', - top: 0, - left: 0, - width: 0, - height: 0, - scaleX: 1, - scaleY: 1, - flipX: false, - flipY: false, - opacity: 1, - angle: 0, - skewX: 0, - skewY: 0, - cornerSize: 13, - touchCornerSize: 24, - transparentCorners: true, - hoverCursor: null, - moveCursor: null, - padding: 0, - borderColor: 'rgb(178,204,255)', - borderDashArray: null, - cornerColor: 'rgb(178,204,255)', - cornerStrokeColor: '', - cornerStyle: 'rect', - cornerDashArray: null, - centeredScaling: false, - centeredRotation: true, - fill: 'rgb(0,0,0)', - fillRule: 'nonzero', - globalCompositeOperation: 'source-over', - backgroundColor: '', - selectionBackgroundColor: '', - stroke: null, - strokeWidth: 1, - strokeDashArray: null, - strokeDashOffset: 0, - strokeLineCap: 'butt', - strokeLineJoin: 'miter', - strokeMiterLimit: 4, - shadow: null, - borderOpacityWhenMoving: 0.4, - borderScaleFactor: 1, - minScaleLimit: 0, - selectable: true, - evented: true, - visible: true, - hasControls: true, - hasBorders: true, - perPixelTargetFind: false, - includeDefaultValues: true, - lockMovementX: false, - lockMovementY: false, - lockRotation: false, - lockScalingX: false, - lockScalingY: false, - lockSkewingX: false, - lockSkewingY: false, - lockScalingFlip: false, - excludeFromExport: false, - objectCaching: !getEnv().isLikelyNode, - noScaleCache: true, - strokeUniform: false, - dirty: true, - __corner: 0, - paintFirst: 'fill', - activeOn: 'down', - stateProperties: [ - 'top', - 'left', - 'scaleX', - 'scaleY', - 'flipX', - 'flipY', - 'originX', - 'originY', - 'angle', - 'opacity', - 'globalCompositeOperation', - 'shadow', - 'visible', - 'skewX', - 'skewY', - ], - cacheProperties: [ - 'fill', - 'stroke', - 'strokeWidth', - 'strokeDashArray', - 'width', - 'height', - 'paintFirst', - 'strokeUniform', - 'strokeLineCap', - 'strokeDashOffset', - 'strokeLineJoin', - 'strokeMiterLimit', - 'backgroundColor', - 'clipPath', - ], - colorProperties: ['fill', 'stroke', 'backgroundColor'], - clipPath: undefined, - inverted: false, - absolutePositioned: false, -}; - -Object.assign(FabricObject.prototype, fabricObjectDefaultValues); +/* + * Properties that at minimum needs to stay on the prototype + * That shouldn't be either on the instance and that can't be used as static + * For inheritance reasons ( used in the superclass but static in the subclass ) + */ +Object.assign(FabricObject.prototype, { + cacheProperties, + stateProperties, + ...fabricObjectDefaultValues, +}); classRegistry.setClass(FabricObject); diff --git a/src/shapes/Object/ObjectGeometry.ts b/src/shapes/Object/ObjectGeometry.ts index 5656c65a373..1d8fadd1a02 100644 --- a/src/shapes/Object/ObjectGeometry.ts +++ b/src/shapes/Object/ObjectGeometry.ts @@ -53,21 +53,21 @@ export class ObjectGeometry< * @type Boolean * @default false */ - flipX: boolean; + declare flipX: boolean; /** * When true, an object is rendered as flipped vertically * @type Boolean * @default false */ - flipY: boolean; + declare flipY: boolean; /** * Padding between object and its controlling borders (in pixels) * @type Number * @default 0 */ - padding: number; + declare padding: number; /** * Describe object's corner position in canvas object absolute coordinates @@ -80,7 +80,7 @@ export class ObjectGeometry< * The coordinates get updated with @method setCoords. * You can calculate them without updating with @method calcACoords(); */ - aCoords: TACoords; + declare aCoords: TACoords; /** * Describe object's corner position in canvas element coordinates. @@ -89,17 +89,17 @@ export class ObjectGeometry< * Those could go away * @todo investigate how to get rid of those */ - lineCoords: TCornerPoint; + declare lineCoords: TCornerPoint; /** * storage cache for object transform matrix */ - ownMatrixCache?: TMatrixCache; + declare ownMatrixCache?: TMatrixCache; /** * storage cache for object full transform matrix */ - matrixCache?: TMatrixCache; + declare matrixCache?: TMatrixCache; /** * A Reference of the Canvas where the object is actually added @@ -107,7 +107,7 @@ export class ObjectGeometry< * @default undefined * @private */ - canvas?: StaticCanvas | Canvas; + declare canvas?: StaticCanvas | Canvas; /** * @returns {number} x position according to object's {@link originX} property in canvas coordinate plane diff --git a/src/shapes/Object/ObjectOrigin.ts b/src/shapes/Object/ObjectOrigin.ts index 4fbd4c7599e..ee082f13cad 100644 --- a/src/shapes/Object/ObjectOrigin.ts +++ b/src/shapes/Object/ObjectOrigin.ts @@ -13,56 +13,56 @@ export class ObjectOrigin extends CommonMethods { * @type Number * @default 0 */ - top: number; + declare top: number; /** * Left position of an object. Note that by default it's relative to object left. You can change this by setting originX={left/center/right} * @type Number * @default 0 */ - left: number; + declare left: number; /** * Object width * @type Number * @default */ - width: number; + declare width: number; /** * Object height * @type Number * @default */ - height: number; + declare height: number; /** * Object scale factor (horizontal) * @type Number * @default 1 */ - scaleX: number; + declare scaleX: number; /** * Object scale factor (vertical) * @type Number * @default 1 */ - scaleY: number; + declare scaleY: number; /** * Angle of skew on x axes of an object (in degrees) * @type Number * @default 0 */ - skewX: number; + declare skewX: number; /** * Angle of skew on y axes of an object (in degrees) * @type Number * @default 0 */ - skewY: number; + declare skewY: number; /** * Horizontal origin of transformation of an object (one of "left", "right", "center") @@ -70,7 +70,7 @@ export class ObjectOrigin extends CommonMethods { * @type String * @default 'left' */ - originX: TOriginX; + declare originX: TOriginX; /** * Vertical origin of transformation of an object (one of "top", "bottom", "center") @@ -78,21 +78,21 @@ export class ObjectOrigin extends CommonMethods { * @type String * @default 'top' */ - originY: TOriginY; + declare originY: TOriginY; /** * Angle of rotation of an object (in degrees) * @type Number * @default 0 */ - angle: TDegree; + declare angle: TDegree; /** * Width of a stroke used to render this object * @type Number * @default 1 */ - strokeWidth: number; + declare strokeWidth: number; /** * When `false`, the stoke width will scale with the object. @@ -105,17 +105,17 @@ export class ObjectOrigin extends CommonMethods { * @type Boolean * @default false */ - strokeUniform: boolean; + declare strokeUniform: boolean; /** * Object containing this object. * can influence its size and position */ - group?: Group; + declare group?: Group; - _originalOriginX?: TOriginX; + declare _originalOriginX?: TOriginX; - _originalOriginY?: TOriginY; + declare _originalOriginY?: TOriginY; /** * Calculate object bounding box dimensions from its properties scale, skew. diff --git a/src/shapes/Object/defaultValues.ts b/src/shapes/Object/defaultValues.ts new file mode 100644 index 00000000000..212e1356317 --- /dev/null +++ b/src/shapes/Object/defaultValues.ts @@ -0,0 +1,112 @@ +import { getEnv } from '../../env'; + +export const stateProperties = [ + 'top', + 'left', + 'scaleX', + 'scaleY', + 'flipX', + 'flipY', + 'originX', + 'originY', + 'angle', + 'opacity', + 'globalCompositeOperation', + 'shadow', + 'visible', + 'skewX', + 'skewY', +]; + +export const cacheProperties = [ + 'fill', + 'stroke', + 'strokeWidth', + 'strokeDashArray', + 'width', + 'height', + 'paintFirst', + 'strokeUniform', + 'strokeLineCap', + 'strokeDashOffset', + 'strokeLineJoin', + 'strokeMiterLimit', + 'backgroundColor', + 'clipPath', +]; + +export const fabricObjectDefaultValues = { + type: 'object', + originX: 'left', + originY: 'top', + top: 0, + left: 0, + width: 0, + height: 0, + scaleX: 1, + scaleY: 1, + flipX: false, + flipY: false, + opacity: 1, + angle: 0, + skewX: 0, + skewY: 0, + cornerSize: 13, + touchCornerSize: 24, + transparentCorners: true, + hoverCursor: null, + moveCursor: null, + padding: 0, + borderColor: 'rgb(178,204,255)', + borderDashArray: null, + cornerColor: 'rgb(178,204,255)', + cornerStrokeColor: '', + cornerStyle: 'rect', + cornerDashArray: null, + centeredScaling: false, + centeredRotation: true, + fill: 'rgb(0,0,0)', + fillRule: 'nonzero', + globalCompositeOperation: 'source-over', + backgroundColor: '', + selectionBackgroundColor: '', + stroke: null, + strokeWidth: 1, + strokeDashArray: null, + strokeDashOffset: 0, + strokeLineCap: 'butt', + strokeLineJoin: 'miter', + strokeMiterLimit: 4, + shadow: null, + borderOpacityWhenMoving: 0.4, + borderScaleFactor: 1, + minScaleLimit: 0, + selectable: true, + evented: true, + visible: true, + hasControls: true, + hasBorders: true, + perPixelTargetFind: false, + includeDefaultValues: true, + lockMovementX: false, + lockMovementY: false, + lockRotation: false, + lockScalingX: false, + lockScalingY: false, + lockSkewingX: false, + lockSkewingY: false, + lockScalingFlip: false, + excludeFromExport: false, + objectCaching: !getEnv().isLikelyNode, + noScaleCache: true, + strokeUniform: false, + dirty: true, + __corner: 0, + paintFirst: 'fill', + activeOn: 'down', + colorProperties: ['fill', 'stroke', 'backgroundColor'], + clipPath: undefined, + inverted: false, + absolutePositioned: false, + FX_DURATION: 500, +}; diff --git a/src/shapes/active_selection.class.ts b/src/shapes/active_selection.class.ts index 993a53ae698..0378c11a2e3 100644 --- a/src/shapes/active_selection.class.ts +++ b/src/shapes/active_selection.class.ts @@ -1,7 +1,7 @@ import { ControlRenderingStyleOverride } from '../controls/controls.render'; import { TClassProperties } from '../typedefs'; import { classRegistry } from '../util/class_registry'; -import { Group, groupDefaultValues } from './group.class'; +import { Group } from './group.class'; import type { FabricObject } from './Object/FabricObject'; export class ActiveSelection extends Group { @@ -147,11 +147,7 @@ export class ActiveSelection extends Group { export const activeSelectionDefaultValues: Partial< TClassProperties > = { - ...groupDefaultValues, type: 'activeSelection', - layout: 'fit-content', - subTargetCheck: false, - interactive: false, }; Object.assign(ActiveSelection.prototype, activeSelectionDefaultValues); diff --git a/src/shapes/circle.class.ts b/src/shapes/circle.class.ts index 6448efa1a1d..89def99132d 100644 --- a/src/shapes/circle.class.ts +++ b/src/shapes/circle.class.ts @@ -1,19 +1,19 @@ import { SHARED_ATTRIBUTES } from '../parser/attributes'; import { parseAttributes } from '../parser/parseAttributes'; -import { TClassProperties } from '../typedefs'; import { cos } from '../util/misc/cos'; import { degreesToRadians } from '../util/misc/radiansDegreesConversion'; import { sin } from '../util/misc/sin'; import { classRegistry } from '../util/class_registry'; -import { FabricObject, fabricObjectDefaultValues } from './Object/FabricObject'; +import { FabricObject, cacheProperties } from './Object/FabricObject'; +import { TClassProperties } from '../typedefs'; export class Circle extends FabricObject { /** * Radius of this circle * @type Number - * @default + * @default 0 */ - radius: number; + declare radius: number; /** * degrees of start of the circle. @@ -21,7 +21,7 @@ export class Circle extends FabricObject { * @type Number 0 - 359 * @default 0 */ - startAngle: number; + declare startAngle: number; /** * End angle of the circle @@ -29,7 +29,7 @@ export class Circle extends FabricObject { * @type Number 1 - 360 * @default 360 */ - endAngle: number; + declare endAngle: number; /** * @private @@ -92,7 +92,7 @@ export class Circle extends FabricObject { * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output * @return {Object} object representation of an instance */ - toObject(propertiesToInclude: (keyof this)[] = []): object { + toObject(propertiesToInclude: string[] = []): object { return super.toObject([ 'radius', 'startAngle', @@ -194,15 +194,12 @@ export const circleDefaultValues: Partial> = { radius: 0, startAngle: 0, endAngle: 360, - cacheProperties: [ - ...fabricObjectDefaultValues.cacheProperties, - 'radius', - 'startAngle', - 'endAngle', - ], }; -Object.assign(Circle.prototype, circleDefaultValues); +Object.assign(Circle.prototype, { + ...circleDefaultValues, + cacheProperties: [...cacheProperties, 'radius', 'startAngle', 'endAngle'], +}); classRegistry.setClass(Circle); classRegistry.setSVGClass(Circle); diff --git a/src/shapes/ellipse.class.ts b/src/shapes/ellipse.class.ts index 0060a5307eb..19f0313a352 100644 --- a/src/shapes/ellipse.class.ts +++ b/src/shapes/ellipse.class.ts @@ -3,7 +3,7 @@ import { SHARED_ATTRIBUTES } from '../parser/attributes'; import { parseAttributes } from '../parser/parseAttributes'; import { TClassProperties } from '../typedefs'; import { classRegistry } from '../util/class_registry'; -import { FabricObject, fabricObjectDefaultValues } from './Object/FabricObject'; +import { FabricObject, cacheProperties } from './Object/FabricObject'; export class Ellipse extends FabricObject { /** @@ -11,14 +11,14 @@ export class Ellipse extends FabricObject { * @type Number * @default */ - rx: number; + declare rx: number; /** * Vertical radius * @type Number * @default */ - ry: number; + declare ry: number; /** * Constructor @@ -27,8 +27,6 @@ export class Ellipse extends FabricObject { */ constructor(options: Record) { super(options); - this.set('rx', (options && options.rx) || 0); - this.set('ry', (options && options.ry) || 0); } /** @@ -74,7 +72,7 @@ export class Ellipse extends FabricObject { * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output * @return {Object} object representation of an instance */ - toObject(propertiesToInclude: (keyof this)[] = []) { + toObject(propertiesToInclude: string[] = []) { return super.toObject(['rx', 'ry', ...propertiesToInclude]); } @@ -145,10 +143,12 @@ export const ellipseDefaultValues: Partial> = { type: 'ellipse', rx: 0, ry: 0, - cacheProperties: [...fabricObjectDefaultValues.cacheProperties, 'rx', 'ry'], }; -Object.assign(Ellipse.prototype, ellipseDefaultValues); +Object.assign(Ellipse.prototype, { + ...ellipseDefaultValues, + cacheProperties: [...cacheProperties, 'rx', 'ry'], +}); classRegistry.setClass(Ellipse); classRegistry.setSVGClass(Ellipse); diff --git a/src/shapes/group.class.ts b/src/shapes/group.class.ts index 596ee2178f2..655d083a042 100644 --- a/src/shapes/group.class.ts +++ b/src/shapes/group.class.ts @@ -17,7 +17,7 @@ import { import { applyTransformToObject } from '../util/misc/objectTransforms'; import { degreesToRadians } from '../util/misc/radiansDegreesConversion'; import { sin } from '../util/misc/sin'; -import { FabricObject, fabricObjectDefaultValues } from './Object/FabricObject'; +import { FabricObject, stateProperties } from './Object/FabricObject'; import { Rect } from './rect.class'; import { classRegistry } from '../util/class_registry'; @@ -90,7 +90,7 @@ export class Group extends createCollectionMixin(FabricObject) { * @type LayoutStrategy * @default */ - layout: LayoutStrategy; + declare layout: LayoutStrategy; /** * Used to optimize performance @@ -98,7 +98,7 @@ export class Group extends createCollectionMixin(FabricObject) { * @default * @type boolean */ - subTargetCheck: boolean; + declare subTargetCheck: boolean; /** * Used to allow targeting of object inside groups. @@ -107,7 +107,7 @@ export class Group extends createCollectionMixin(FabricObject) { * @default * @type boolean */ - interactive: boolean; + declare interactive: boolean; /** * Used internally to optimize performance @@ -130,7 +130,7 @@ export class Group extends createCollectionMixin(FabricObject) { objectsRelativeToGroup?: boolean ) { super(); - this._objects = objects || []; + this._objects = objects; this.__objectMonitor = this.__objectMonitor.bind(this); this.__objectSelectionTracker = this.__objectSelectionMonitor.bind( this, @@ -141,7 +141,7 @@ export class Group extends createCollectionMixin(FabricObject) { false ); this._firstLayoutDone = false; - // setting angle, skewX, skewY must occur after initial layout + // setting angle, skewX, skewY must occur after initial layout this.set({ ...options, angle: 0, skewX: 0, skewY: 0 }); this.forEachObject((object) => { this.enterGroup(object, false); @@ -913,7 +913,7 @@ export class Group extends createCollectionMixin(FabricObject) { */ __serializeObjects( method: 'toObject' | 'toDatalessObject', - propertiesToInclude?: (keyof this)[] + propertiesToInclude?: string[] ) { const _includeDefaultValues = this.includeDefaultValues; return this._objects @@ -1041,10 +1041,13 @@ export const groupDefaultValues: Partial> = { type: 'group', layout: 'fit-content', strokeWidth: 0, - stateProperties: fabricObjectDefaultValues.stateProperties.concat('layout'), subTargetCheck: false, interactive: false, }; -Object.assign(Group.prototype, groupDefaultValues); +Object.assign(Group.prototype, { + ...groupDefaultValues, + stateProperties: [...stateProperties, 'layout'], +}); + classRegistry.setClass(Group); diff --git a/src/shapes/image.class.ts b/src/shapes/image.class.ts index 40e9a1a99e7..346b2eb6b51 100644 --- a/src/shapes/image.class.ts +++ b/src/shapes/image.class.ts @@ -17,7 +17,7 @@ import { } from '../util/misc/objectEnlive'; import { parsePreserveAspectRatioAttribute } from '../util/misc/svgParsing'; import { classRegistry } from '../util/class_registry'; -import { FabricObject, fabricObjectDefaultValues } from './Object/FabricObject'; +import { FabricObject, cacheProperties } from './Object/FabricObject'; export type ImageSource = | HTMLImageElement @@ -33,9 +33,9 @@ export class Image extends FabricObject { * This allows for relative urls as image src. * @since 2.7.0 * @type Boolean - * @default + * @default false */ - srcFromAttribute: boolean; + declare srcFromAttribute: boolean; /** * private @@ -73,7 +73,7 @@ export class Image extends FabricObject { * number bigger than 1 are not implemented yet. * @type Number */ - minimumScaleTrigger: number; + declare minimumScaleTrigger: number; /** * key used to retrieve the texture representing this image @@ -81,7 +81,7 @@ export class Image extends FabricObject { * @type String * @default */ - cacheKey: string; + declare cacheKey: string; /** * Image crop in pixels from original image size. @@ -89,7 +89,7 @@ export class Image extends FabricObject { * @type Number * @default */ - cropX: number; + declare cropX: number; /** * Image crop in pixels from original image size. @@ -97,7 +97,7 @@ export class Image extends FabricObject { * @type Number * @default */ - cropY: number; + declare cropY: number; /** * Indicates whether this canvas will use image smoothing when painting this image. @@ -106,18 +106,18 @@ export class Image extends FabricObject { * @type Boolean * @default */ - imageSmoothing: boolean; + declare imageSmoothing: boolean; - preserveAspectRatio: string; + declare preserveAspectRatio: string; - protected src: string; + protected declare src: string; - filters: BaseFilter[]; - resizeFilter: BaseFilter; + declare filters: BaseFilter[]; + declare resizeFilter: BaseFilter; - protected _element: ImageSource; - protected _originalElement: ImageSource; - protected _filteredEl: ImageSource; + protected declare _element: ImageSource; + protected declare _originalElement: ImageSource; + protected declare _filteredEl: ImageSource; /** * Constructor @@ -131,10 +131,8 @@ export class Image extends FabricObject { constructor(elementId: string, options: any = {}); constructor(element: ImageSource, options: any = {}); constructor(arg0: ImageSource | string, options: any = {}) { - super(); - this.filters = []; + super({ filters: [], ...options }); this.cacheKey = `texture${uid()}`; - this.set(options); this.setElement( (typeof arg0 === 'string' && getEnv().document.getElementById(arg0)) || arg0, @@ -777,17 +775,15 @@ export const imageDefaultValues: Partial> = { strokeWidth: 0, srcFromAttribute: false, minimumScaleTrigger: 0.5, - cacheProperties: [ - ...fabricObjectDefaultValues.cacheProperties, - 'cropX', - 'cropY', - ], cropX: 0, cropY: 0, imageSmoothing: true, }; -Object.assign(Image.prototype, imageDefaultValues); +Object.assign(Image.prototype, { + ...imageDefaultValues, + cacheProperties: [...cacheProperties, 'cropX', 'cropY'], +}); classRegistry.setClass(Image); classRegistry.setSVGClass(Image); diff --git a/src/shapes/itext.class.ts b/src/shapes/itext.class.ts index 6a0b5e63520..09bb0c2082e 100644 --- a/src/shapes/itext.class.ts +++ b/src/shapes/itext.class.ts @@ -67,53 +67,53 @@ export class IText extends ITextClickBehaviorMixin { * @type Number * @default */ - selectionStart: number; + declare selectionStart: number; /** * Index where text selection ends * @type Number * @default */ - selectionEnd: number; + declare selectionEnd: number; - compositionStart: number; + declare compositionStart: number; - compositionEnd: number; + declare compositionEnd: number; /** * Color of text selection * @type String * @default */ - selectionColor: string; + declare selectionColor: string; /** * Indicates whether text is in editing mode * @type Boolean * @default */ - isEditing: boolean; + declare isEditing: boolean; /** * Indicates whether a text can be edited * @type Boolean * @default */ - editable: boolean; + declare editable: boolean; /** * Border color of text object while it's in editing mode * @type String * @default */ - editingBorderColor: string; + declare editingBorderColor: string; /** * Width of cursor (in px) * @type Number * @default */ - cursorWidth: number; + declare cursorWidth: number; /** * Color of text cursor color in editing mode. @@ -123,28 +123,28 @@ export class IText extends ITextClickBehaviorMixin { * @type String * @default */ - cursorColor: string; + declare cursorColor: string; /** * Delay between cursor blink (in ms) * @type Number * @default */ - cursorDelay: number; + declare cursorDelay: number; /** * Duration of cursor fade in (in ms) * @type Number * @default */ - cursorDuration: number; + declare cursorDuration: number; /** * Indicates whether internal text char widths can be cached * @type Boolean * @default */ - caching: boolean; + declare caching: boolean; /** @@ -619,9 +619,9 @@ export const iTextDefaultValues: Partial> = { cursorDuration: 600, caching: true, hiddenTextareaContainer: null, - _reSpace: /\s|\n/, _currentCursorOpacity: 1, _selectionDirection: null, + _reSpace: /\s|\n/, inCompositionMode: false, keysMap, keysMapRtl, diff --git a/src/shapes/line.class.ts b/src/shapes/line.class.ts index 467d9adb5cf..bd1798b9767 100644 --- a/src/shapes/line.class.ts +++ b/src/shapes/line.class.ts @@ -1,12 +1,15 @@ -// @ts-nocheck import { SHARED_ATTRIBUTES } from '../parser/attributes'; import { parseAttributes } from '../parser/parseAttributes'; import { TClassProperties } from '../typedefs'; import { clone } from '../util/lang_object'; import { classRegistry } from '../util/class_registry'; -import { FabricObject, fabricObjectDefaultValues } from './Object/FabricObject'; +import { FabricObject, cacheProperties } from './Object/FabricObject'; +import { Point } from '../point.class'; +import { isFiller } from '../util/types'; -const coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 }; +// @TODO this code is terrible and Line should be a special case of polyline. + +const coordProps = ['x1', 'x2', 'y1', 'y2']; export class Line extends FabricObject { /** @@ -14,28 +17,28 @@ export class Line extends FabricObject { * @type Number * @default */ - x1: number; + declare x1: number; /** * y value or first line edge * @type Number * @default */ - y1: number; + declare y1: number; /** * x value or second line edge * @type Number * @default */ - x2: number; + declare x2: number; /** * y value or second line edge * @type Number * @default */ - y2: number; + declare y2: number; /** * Constructor @@ -43,11 +46,10 @@ export class Line extends FabricObject { * @param {Object} [options] Options object * @return {Line} thisArg */ - constructor(points, options) { - if (!points) { - points = [0, 0, 0, 0]; - } - + constructor( + points = [0, 0, 0, 0], + options: Partial> = {} + ) { super(options); this.set('x1', points[0]); @@ -62,15 +64,11 @@ export class Line extends FabricObject { * @private * @param {Object} [options] Options */ - _setWidthHeight(options) { - options || (options = {}); - + _setWidthHeight({ left, top }: Partial> = {}) { this.width = Math.abs(this.x2 - this.x1); this.height = Math.abs(this.y2 - this.y1); - - this.left = 'left' in options ? options.left : this._getLeftToOriginX(); - - this.top = 'top' in options ? options.top : this._getTopToOriginY(); + this.left = left ?? this._getLeftToOriginX(); + this.top = top ?? this._getTopToOriginY(); } /** @@ -78,9 +76,9 @@ export class Line extends FabricObject { * @param {String} key * @param {*} value */ - _set(key, value) { + _set(key: string, value: any) { super._set(key, value); - if (typeof coordProps[key] !== 'undefined') { + if (coordProps.includes(key)) { this._setWidthHeight(); } return this; @@ -90,7 +88,7 @@ export class Line extends FabricObject { * @private * @param {CanvasRenderingContext2D} ctx Context to render on */ - _render(ctx) { + _render(ctx: CanvasRenderingContext2D) { ctx.beginPath(); const p = this.calcLinePoints(); @@ -103,7 +101,11 @@ export class Line extends FabricObject { // make sure setting "fill" changes color of a line // (by copying fillStyle to strokeStyle, since line is stroked, not filled) const origStrokeStyle = ctx.strokeStyle; - ctx.strokeStyle = this.stroke || ctx.fillStyle; + if (isFiller(this.stroke)) { + ctx.strokeStyle = this.stroke.toLive(ctx); + } else { + ctx.strokeStyle = this.stroke ?? ctx.fillStyle; + } this.stroke && this._renderStroke(ctx); ctx.strokeStyle = origStrokeStyle; } @@ -112,13 +114,10 @@ export class Line extends FabricObject { * This function is an helper for svg import. it returns the center of the object in the svg * untransformed coordinates * @private - * @return {Object} center point from element coordinates + * @return {Point} center point from element coordinates */ - _findCenterFromElement() { - return { - x: (this.x1 + this.x2) / 2, - y: (this.y1 + this.y2) / 2, - }; + _findCenterFromElement(): Point { + return new Point((this.x1 + this.x2) / 2, (this.y1 + this.y2) / 2); } /** @@ -127,7 +126,7 @@ export class Line extends FabricObject { * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output * @return {Object} object representation of an instance */ - toObject(propertiesToInclude) { + toObject(propertiesToInclude: string[]) { return { ...super.toObject(propertiesToInclude), ...this.calcLinePoints() }; } @@ -135,7 +134,7 @@ export class Line extends FabricObject { * Calculate object dimensions from its properties * @private */ - _getNonTransformedDimensions() { + _getNonTransformedDimensions(): Point { const dim = super._getNonTransformedDimensions(); if (this.strokeLineCap === 'butt') { if (this.width === 0) { @@ -152,7 +151,7 @@ export class Line extends FabricObject { * Recalculates line points given width and height * @private */ - calcLinePoints() { + calcLinePoints(): Record { const xMult = this.x1 <= this.x2 ? -1 : 1, yMult = this.y1 <= this.y2 ? -1 : 1, x1 = xMult * this.width * 0.5, @@ -168,7 +167,10 @@ export class Line extends FabricObject { }; } - private makeEdgeToOriginGetter(propertyNames, originValues) { + private makeEdgeToOriginGetter( + propertyNames: any, + originValues: any + ): number { const origin = propertyNames.origin, axis1 = propertyNames.axis1, axis2 = propertyNames.axis2, @@ -186,6 +188,9 @@ export class Line extends FabricObject { ); case farthest: return Math.max(this.get(axis1), this.get(axis2)); + // this should never occurr, since origin is always either one of the 3 above + default: + return 0; } } @@ -193,7 +198,7 @@ export class Line extends FabricObject { * @private * @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line. */ - _getLeftToOriginX() { + _getLeftToOriginX(): number { return this.makeEdgeToOriginGetter( { // property names @@ -215,7 +220,7 @@ export class Line extends FabricObject { * @private * @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line. */ - _getTopToOriginY() { + _getTopToOriginY(): number { return this.makeEdgeToOriginGetter( { // property names @@ -273,8 +278,7 @@ export class Line extends FabricObject { * @param {Object} [options] Options object * @param {Function} [callback] callback function invoked after parsing */ - static fromElement(element, callback, options) { - options = options || {}; + static fromElement(element: SVGElement, callback: (line: Line) => any) { const parsedAttributes = parseAttributes(element, this.ATTRIBUTE_NAMES), points = [ parsedAttributes.x1 || 0, @@ -282,7 +286,7 @@ export class Line extends FabricObject { parsedAttributes.x2 || 0, parsedAttributes.y2 || 0, ]; - callback(new this(points, { ...parsedAttributes, ...options })); + callback(new this(points, parsedAttributes)); } /* _FROM_SVG_END_ */ @@ -294,13 +298,12 @@ export class Line extends FabricObject { * @param {Object} object Object to create an instance from * @returns {Promise} */ - static fromObject(object) { + static fromObject(object: Record) { const options = clone(object, true); options.points = [object.x1, object.y1, object.x2, object.y2]; return this._fromObject(options, { extraParam: 'points', }).then((fabricLine) => { - delete fabricLine.points; return fabricLine; }); } @@ -312,16 +315,12 @@ export const lineDefaultValues: Partial> = { y1: 0, x2: 0, y2: 0, - cacheProperties: [ - ...fabricObjectDefaultValues.cacheProperties, - 'x1', - 'x2', - 'y1', - 'y2', - ], }; -Object.assign(Line.prototype, lineDefaultValues); +Object.assign(Line.prototype, { + ...lineDefaultValues, + cacheProperties: [...cacheProperties, 'x1', 'x2', 'y1', 'y2'], +}); classRegistry.setClass(Line); classRegistry.setSVGClass(Line); diff --git a/src/shapes/path.class.ts b/src/shapes/path.class.ts index adaca3913d6..7832834b464 100644 --- a/src/shapes/path.class.ts +++ b/src/shapes/path.class.ts @@ -14,7 +14,7 @@ import { type TPathSegmentsInfo, } from '../util/path'; import { classRegistry } from '../util/class_registry'; -import { FabricObject, fabricObjectDefaultValues } from './Object/FabricObject'; +import { FabricObject, cacheProperties } from './Object/FabricObject'; export class Path extends FabricObject { /** @@ -22,15 +22,15 @@ export class Path extends FabricObject { * @type Array * @default */ - path: PathData; + declare path: PathData; - pathOffset: Point; + declare pathOffset: Point; - fromSVG?: boolean; + declare fromSVG?: boolean; - sourcePath?: string; + declare sourcePath?: string; - segmentsInfo?: TPathSegmentsInfo[]; + declare segmentsInfo?: TPathSegmentsInfo[]; /** * Constructor @@ -43,7 +43,6 @@ export class Path extends FabricObject { { path: _, left, top, ...options }: any = {} ) { super(options); - const pathTL = this._setPath(path || []); const origin = this.translateToGivenOrigin( new Point(left ?? pathTL.x, top ?? pathTL.y), @@ -383,15 +382,12 @@ export class Path extends FabricObject { export const pathDefaultValues: Partial> = { type: 'path', - path: null, - cacheProperties: [ - ...fabricObjectDefaultValues.cacheProperties, - 'path', - 'fillRule', - ], }; -Object.assign(Path.prototype, pathDefaultValues); +Object.assign(Path.prototype, { + ...pathDefaultValues, + cacheProperties: [...cacheProperties, 'path', 'fillRule'], +}); classRegistry.setClass(Path); classRegistry.setSVGClass(Path); diff --git a/src/shapes/polyline.class.ts b/src/shapes/polyline.class.ts index c4a223e2dd2..941dde47719 100644 --- a/src/shapes/polyline.class.ts +++ b/src/shapes/polyline.class.ts @@ -9,7 +9,7 @@ import { makeBoundingBoxFromPoints } from '../util/misc/boundingBoxFromPoints'; import { projectStrokeOnPoints } from '../util/misc/projectStroke'; import { degreesToRadians } from '../util/misc/radiansDegreesConversion'; import { toFixed } from '../util/misc/toFixed'; -import { FabricObject, fabricObjectDefaultValues } from './Object/FabricObject'; +import { FabricObject, cacheProperties } from './Object/FabricObject'; export class Polyline extends FabricObject { /** @@ -17,7 +17,7 @@ export class Polyline extends FabricObject { * @type Array * @default */ - points: IPoint[]; + declare points: IPoint[]; /** * WARNING: Feature in progress @@ -27,23 +27,22 @@ export class Polyline extends FabricObject { * @deprecated * @type Boolean * @default false - * @todo set default to true and remove flag and related logic */ - exactBoundingBox: boolean; + declare exactBoundingBox: boolean; - private initialized: true | undefined; + private declare initialized: true | undefined; /** * A list of properties that if changed trigger a recalculation of dimensions * @todo check if you really need to recalculate for all cases */ - strokeBBoxAffectingProperties: (keyof this)[]; + declare strokeBBoxAffectingProperties: (keyof this)[]; - fromSVG: boolean; + declare fromSVG: boolean; - pathOffset: Point; + declare pathOffset: Point; - strokeOffset: Point; + declare strokeOffset: Point; /** * Constructor @@ -187,7 +186,7 @@ export class Polyline extends FabricObject { * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output * @return {Object} Object representation of an instance */ - toObject(propertiesToInclude?: (keyof this)[]): object { + toObject(propertiesToInclude?: string[]): object { return { ...super.toObject(propertiesToInclude), points: this.points.concat(), @@ -313,7 +312,11 @@ export class Polyline extends FabricObject { export const polylineDefaultValues: Partial> = { type: 'polyline', exactBoundingBox: false, - cacheProperties: [...fabricObjectDefaultValues.cacheProperties, 'points'], +}; + +Object.assign(Polyline.prototype, { + ...polylineDefaultValues, + cacheProperties: [...cacheProperties, 'points'], strokeBBoxAffectingProperties: [ 'skewX', 'skewY', @@ -324,9 +327,7 @@ export const polylineDefaultValues: Partial> = { 'strokeUniform', 'points', ], -}; - -Object.assign(Polyline.prototype, polylineDefaultValues); +}); classRegistry.setClass(Polyline); classRegistry.setSVGClass(Polyline); diff --git a/src/shapes/rect.class.ts b/src/shapes/rect.class.ts index 0316d934958..38e71a43d7c 100644 --- a/src/shapes/rect.class.ts +++ b/src/shapes/rect.class.ts @@ -3,7 +3,7 @@ import { SHARED_ATTRIBUTES } from '../parser/attributes'; import { parseAttributes } from '../parser/parseAttributes'; import { TClassProperties } from '../typedefs'; import { classRegistry } from '../util/class_registry'; -import { FabricObject, fabricObjectDefaultValues } from './Object/FabricObject'; +import { FabricObject, cacheProperties } from './Object/FabricObject'; export class Rect extends FabricObject { /** @@ -11,14 +11,14 @@ export class Rect extends FabricObject { * @type Number * @default */ - rx: number; + declare rx: number; /** * Vertical border radius * @type Number * @default */ - ry: number; + declare ry: number; /** * Constructor @@ -106,7 +106,7 @@ export class Rect extends FabricObject { * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output * @return {Object} object representation of an instance */ - toObject(propertiesToInclude: (keyof this)[] = []) { + toObject(propertiesToInclude: string[] = []) { return super.toObject(['rx', 'ry', ...propertiesToInclude]); } @@ -188,10 +188,12 @@ export const rectDefaultValues: Partial> = { type: 'rect', rx: 0, ry: 0, - cacheProperties: [...fabricObjectDefaultValues.cacheProperties, 'rx', 'ry'], }; -Object.assign(Rect.prototype, rectDefaultValues); +Object.assign(Rect.prototype, { + ...rectDefaultValues, + cacheProperties: [...cacheProperties, 'rx', 'ry'], +}); classRegistry.setClass(Rect); classRegistry.setSVGClass(Rect); diff --git a/src/shapes/text.class.ts b/src/shapes/text.class.ts index 9635d3a0b75..8533ef6f81d 100644 --- a/src/shapes/text.class.ts +++ b/src/shapes/text.class.ts @@ -1,5 +1,4 @@ // @ts-nocheck -import { getEnv } from '../env'; import { cache } from '../cache'; import { DEFAULT_SVG_FONT_SIZE } from '../constants'; import { ObjectEvents } from '../EventTypeDefs'; @@ -25,7 +24,7 @@ import { stylesToArray, } from '../util/misc/textStyles'; import { getPathSegmentsInfo, getPointOnPath } from '../util/path'; -import { fabricObjectDefaultValues } from './Object/FabricObject'; +import { cacheProperties } from './Object/FabricObject'; import { Path } from './path.class'; import { TextSVGExportMixin } from '../mixins/text.svg_export'; import { applyMixins } from '../util/applyMixins'; @@ -96,77 +95,77 @@ export class Text< * @type Array * @private */ - _dimensionAffectingProps: (keyof this)[]; + declare _dimensionAffectingProps: (keyof this)[]; /** * @private */ - _reNewline: RegExp; + declare _reNewline: RegExp; /** * Use this regular expression to filter for whitespaces that is not a new line. * Mostly used when text is 'justify' aligned. * @private */ - _reSpacesAndTabs: RegExp; + declare _reSpacesAndTabs: RegExp; /** * Use this regular expression to filter for whitespace that is not a new line. * Mostly used when text is 'justify' aligned. * @private */ - _reSpaceAndTab: RegExp; + declare _reSpaceAndTab: RegExp; /** * Use this regular expression to filter consecutive groups of non spaces. * Mostly used when text is 'justify' aligned. * @private */ - _reWords: RegExp; + declare _reWords: RegExp; - text: string; + declare text: string; /** * Font size (in pixels) * @type Number * @default */ - fontSize: number; + declare fontSize: number; /** * Font weight (e.g. bold, normal, 400, 600, 800) * @type {(Number|String)} * @default */ - fontWeight: string; + declare fontWeight: string; /** * Font family * @type String * @default */ - fontFamily: string; + declare fontFamily: string; /** * Text decoration underline. * @type Boolean * @default */ - underline: boolean; + declare underline: boolean; /** * Text decoration overline. * @type Boolean * @default */ - overline: boolean; + declare overline: boolean; /** * Text decoration linethrough. * @type Boolean * @default */ - linethrough: boolean; + declare linethrough: boolean; /** * Text alignment. Possible values: "left", "center", "right", "justify", @@ -174,26 +173,26 @@ export class Text< * @type String * @default */ - textAlign: string; + declare textAlign: string; /** * Font style . Possible values: "", "normal", "italic" or "oblique". * @type String * @default */ - fontStyle: string; + declare fontStyle: string; /** * Line height * @type Number * @default */ - lineHeight: number; + declare lineHeight: number; /** * Superscript schema object (minimum overlap) */ - superscript: { + declare superscript: { /** * fontSize factor * @default 0.6 @@ -209,7 +208,7 @@ export class Text< /** * Subscript schema object (minimum overlap) */ - subscript: { + declare subscript: { /** * fontSize factor * @default 0.6 @@ -227,11 +226,11 @@ export class Text< * @type String * @default */ - textBackgroundColor: string; + declare textBackgroundColor: string; - protected _styleProperties: string[]; + protected declare _styleProperties: string[]; - styles: TextStyle; + declare styles: TextStyle; /** * Path that the text should follow. @@ -255,7 +254,7 @@ export class Text< * }); * @default */ - path: Path; + declare path: Path; /** * Offset amount for text path starting position @@ -263,7 +262,7 @@ export class Text< * @type Number * @default */ - pathStartOffset: number; + declare pathStartOffset: number; /** * Which side of the path the text should be drawn on. @@ -271,7 +270,7 @@ export class Text< * @type {String} 'left|right' * @default */ - pathSide: string; + declare pathSide: string; /** * How text is aligned to the path. This property determines @@ -281,24 +280,24 @@ export class Text< * @type String * @default */ - pathAlign: string; + declare pathAlign: string; /** * @private */ - _fontSizeFraction: number; + declare _fontSizeFraction: number; /** * @private */ - offsets: { underline: number; linethrough: number; overline: number }; + declare offsets: { underline: number; linethrough: number; overline: number }; /** * Text Line proportion to font Size (in pixels) * @type Number * @default */ - _fontSizeMult: number; + declare _fontSizeMult: number; /** * additional space between characters @@ -306,14 +305,14 @@ export class Text< * @type Number * @default */ - charSpacing: number; + declare charSpacing: number; /** * Baseline shift, styles only, keep at 0 for the main text object * @type {Number} * @default */ - deltaY: number; + declare deltaY: number; /** * WARNING: EXPERIMENTAL. NOT SUPPORTED YET @@ -326,7 +325,7 @@ export class Text< * @type {String} 'ltr|rtl' * @default */ - direction: string; + declare direction: string; /** * contains characters bounding boxes @@ -340,14 +339,14 @@ export class Text< * @readonly * @private */ - CACHE_FONT_SIZE: number; + declare CACHE_FONT_SIZE: number; /** * contains the min text width to avoid getting 0 * @type {Number} * @default */ - MIN_TEXT_WIDTH: number; + declare MIN_TEXT_WIDTH: number; /** * contains the the text of the object, divided in lines as they are displayed @@ -355,23 +354,23 @@ export class Text< * @type {string[]} * @default */ - textLines: string[]; + declare textLines: string[]; /** * same as textlines, but each line is an array of graphemes as split by splitByGrapheme * @type {string[]} * @default */ - _textLines: string[][]; + declare _textLines: string[][]; - _unwrappedTextLines: string[][]; - _text: string[]; - cursorWidth: number; - __lineHeights: number[]; - __lineWidths: number[]; - _forceClearCache: boolean; + declare _unwrappedTextLines: string[][]; + declare _text: string[]; + declare cursorWidth: number; + declare __lineHeights: number[]; + declare __lineWidths: number[]; + declare _forceClearCache: boolean; - initialized?: true; + declare initialized?: true; constructor(text: string, options: any) { super({ ...options, text, styles: options?.styles || {} }); @@ -1869,6 +1868,9 @@ export class Text< } } +// @TODO: Many things here are configuration related and shouldn't be on the class nor prototype +// regexes, list of properties that are not suppose to change by instances, magic consts. +// this will be a separated effort export const textDefaultValues: Partial> = { _dimensionAffectingProps: [ 'fontSize', @@ -1922,10 +1924,7 @@ export const textDefaultValues: Partial> = { baseline: 0.11, // baseline-shift factor (downwards) }, textBackgroundColor: '', - cacheProperties: [ - ...fabricObjectDefaultValues.cacheProperties, - ...additionalProps, - ], + cacheProperties: [...cacheProperties, ...additionalProps], stroke: null, shadow: null, path: null, diff --git a/src/shapes/textbox.class.ts b/src/shapes/textbox.class.ts index d7c9f4aac73..5ff59c1787e 100644 --- a/src/shapes/textbox.class.ts +++ b/src/shapes/textbox.class.ts @@ -16,7 +16,7 @@ export class Textbox extends IText { * @type Number * @default */ - minWidth: number; + declare minWidth: number; /** * Minimum calculated width of a textbox, in pixels. @@ -25,13 +25,13 @@ export class Textbox extends IText { * @type Number * @default */ - dynamicMinWidth: number; + declare dynamicMinWidth: number; /** * Cached array of text wrapping. * @type Array */ - __cachedLines: Array | null = null; + declare __cachedLines: Array | null; /** * Use this boolean property in order to split strings that have no white space concept. @@ -39,7 +39,7 @@ export class Textbox extends IText { * @type Boolean * @since 2.6.0 */ - splitByGrapheme: boolean; + declare splitByGrapheme: boolean; /** * Unlike superclass's version of this function, Textbox does not update @@ -457,6 +457,9 @@ export class Textbox extends IText { } } +// @TODO: Many things here are configuration related and shouldn't be on the class nor prototype +// regexes, list of properties that are not suppose to change by instances, magic consts. +// this will be a separated effort export const textboxDefaultValues: Partial> = { type: 'textbox', minWidth: 20, diff --git a/src/util/class_registry.ts b/src/util/class_registry.ts index eac00402234..808d1b1a33f 100644 --- a/src/util/class_registry.ts +++ b/src/util/class_registry.ts @@ -14,8 +14,8 @@ export const JSON = 'json'; export const SVG = 'svg'; export class ClassRegistry { - [JSON]: Map; - [SVG]: Map; + declare [JSON]: Map; + declare [SVG]: Map; constructor() { this[JSON] = new Map(); diff --git a/src/util/transform_matrix_removal.ts b/src/util/transform_matrix_removal.ts new file mode 100644 index 00000000000..a560726788b --- /dev/null +++ b/src/util/transform_matrix_removal.ts @@ -0,0 +1,60 @@ +import { Image } from '../shapes/image.class'; +import type { FabricObject } from '../shapes/Object/FabricObject'; +import { TMat2D } from '../typedefs'; +import { qrDecompose } from './misc/matrix'; + +type FabricObjectWithTransformMatrix = FabricObject & { + transformMatrix?: TMat2D; +}; + +/** + * This function is an helper for svg import. it decompose the transformMatrix + * and assign properties to object. + * untransformed coordinates + * @private + */ +const _assignTransformMatrixProps = ( + object: FabricObjectWithTransformMatrix +) => { + if (object.transformMatrix) { + const { scaleX, scaleY, angle, skewX } = qrDecompose( + object.transformMatrix + ); + object.flipX = false; + object.flipY = false; + object.set('scaleX', scaleX); + object.set('scaleY', scaleY); + object.angle = angle; + object.skewX = skewX; + object.skewY = 0; + } +}; + +/** + * This function is an helper for svg import. it removes the transform matrix + * and set to object properties that fabricjs can handle + * @private + * @param {Object} preserveAspectRatioOptions + */ +export const removeTransformMatrixForSvgParsing = ( + object: FabricObjectWithTransformMatrix, + preserveAspectRatioOptions?: any +) => { + let center = object._findCenterFromElement(); + if (object.transformMatrix) { + _assignTransformMatrixProps(object); + center = center.transform(object.transformMatrix); + } + delete object.transformMatrix; + if (preserveAspectRatioOptions) { + object.scaleX *= preserveAspectRatioOptions.scaleX; + object.scaleY *= preserveAspectRatioOptions.scaleY; + (object as Image).cropX = preserveAspectRatioOptions.cropX; + (object as Image).cropY = preserveAspectRatioOptions.cropY; + center.x += preserveAspectRatioOptions.offsetLeft; + center.y += preserveAspectRatioOptions.offsetTop; + object.width = preserveAspectRatioOptions.width; + object.height = preserveAspectRatioOptions.height; + } + object.setPositionByOrigin(center, 'center', 'center'); +}; diff --git a/src/util/types.ts b/src/util/types.ts index a75d6313f85..8d88fc1120d 100644 --- a/src/util/types.ts +++ b/src/util/types.ts @@ -10,11 +10,20 @@ import type { Text } from '../shapes/text.class'; import type { Pattern } from '../pattern.class'; import type { IText } from '../shapes/itext.class'; import type { Textbox } from '../shapes/textbox.class'; +import type { Gradient } from '../gradient/gradient.class'; -export const isFiller = (filler: TFiller | string): filler is TFiller => { +export const isFiller = ( + filler: TFiller | string | null +): filler is TFiller => { return !!filler && (filler as TFiller).toLive !== undefined; }; +export const isSerializableFiller = ( + filler: TFiller | string | null +): filler is TFiller => { + return !!filler && typeof (filler as TFiller).toObject === 'function'; +}; + export const isPattern = (filler: TFiller): filler is Pattern => { return ( !!filler && diff --git a/test/unit/image.js b/test/unit/image.js index 8dc21f65594..21013ce67c8 100644 --- a/test/unit/image.js +++ b/test/unit/image.js @@ -613,7 +613,7 @@ }); fabric.Image.fromElement(imageEl, function(imgObject) { - imgObject._removeTransformMatrix(imgObject.parsePreserveAspectRatioAttribute()); + fabric.util.removeTransformMatrixForSvgParsing(imgObject, imgObject.parsePreserveAspectRatioAttribute()); assert.ok(imgObject instanceof fabric.Image); assert.deepEqual(imgObject.get('width'), 14, 'width of an object'); assert.deepEqual(imgObject.get('height'), 17, 'height of an object'); @@ -641,7 +641,7 @@ }); fabric.Image.fromElement(imageEl, function(imgObject) { - imgObject._removeTransformMatrix(imgObject.parsePreserveAspectRatioAttribute()); + fabric.util.removeTransformMatrixForSvgParsing(imgObject, imgObject.parsePreserveAspectRatioAttribute()); assert.deepEqual(imgObject.get('width'), 14, 'width of an object'); assert.deepEqual(imgObject.get('height'), 17, 'height of an object'); assert.deepEqual(imgObject.get('left'), 0, 'left'); @@ -670,7 +670,7 @@ }); fabric.Image.fromElement(imageEl, function(imgObject) { - imgObject._removeTransformMatrix(imgObject.parsePreserveAspectRatioAttribute()); + fabric.util.removeTransformMatrixForSvgParsing(imgObject, imgObject.parsePreserveAspectRatioAttribute()); assert.deepEqual(imgObject.get('width'), 14, 'width of an object'); assert.deepEqual(imgObject.get('height'), 17, 'height of an object'); assert.deepEqual(imgObject.get('left'), 0, 'left'); @@ -699,7 +699,7 @@ }); fabric.Image.fromElement(imageEl, function(imgObject) { - imgObject._removeTransformMatrix(imgObject.parsePreserveAspectRatioAttribute()); + fabric.util.removeTransformMatrixForSvgParsing(imgObject, imgObject.parsePreserveAspectRatioAttribute()); assert.deepEqual(imgObject.get('width'), 14, 'width of an object'); assert.deepEqual(imgObject.get('height'), 17, 'height of an object'); assert.deepEqual(imgObject.get('left'), 0, 'left'); @@ -728,7 +728,7 @@ }); fabric.Image.fromElement(imageEl, function(imgObject) { - imgObject._removeTransformMatrix(imgObject.parsePreserveAspectRatioAttribute()); + fabric.util.removeTransformMatrixForSvgParsing(imgObject, imgObject.parsePreserveAspectRatioAttribute()); assert.deepEqual(imgObject.get('width'), 14, 'width of an object'); assert.deepEqual(imgObject.get('height'), 17, 'height of an object'); assert.deepEqual(imgObject.get('left'), 0, 'left'); @@ -757,7 +757,7 @@ }); fabric.Image.fromElement(imageEl, function(imgObject) { - imgObject._removeTransformMatrix(imgObject.parsePreserveAspectRatioAttribute()); + fabric.util.removeTransformMatrixForSvgParsing(imgObject, imgObject.parsePreserveAspectRatioAttribute()); assert.deepEqual(imgObject.get('width'), 14, 'width of an object'); assert.deepEqual(imgObject.get('height'), 17, 'height of an object'); assert.deepEqual(imgObject.get('left'), 35, 'left'); @@ -786,7 +786,7 @@ }); fabric.Image.fromElement(imageEl, function(imgObject) { - imgObject._removeTransformMatrix(imgObject.parsePreserveAspectRatioAttribute()); + fabric.util.removeTransformMatrixForSvgParsing(imgObject, imgObject.parsePreserveAspectRatioAttribute()); assert.deepEqual(imgObject.get('width'), 14, 'width of an object'); assert.deepEqual(imgObject.get('height'), 17, 'height of an object'); assert.deepEqual(imgObject.get('left'), 70, 'left'); diff --git a/test/unit/object.js b/test/unit/object.js index f079ee380b9..cc0bdce8b6d 100644 --- a/test/unit/object.js +++ b/test/unit/object.js @@ -630,112 +630,6 @@ assert.ok(removedEventFired); }); - QUnit.test('center', function(assert) { - var object = new fabric.Object(); - object.strokeWidth = 0; - canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; - assert.ok(typeof object.center === 'function'); - - canvas.add(object); - assert.equal(object.center(), object, 'should be chainable'); - - assert.equal(object.getCenterPoint().x, canvas.getWidth() / 2); - assert.equal(object.getCenterPoint().y, canvas.getHeight() / 2); - - canvas.setZoom(2); - object.center(); - assert.equal(object.getCenterPoint().x, canvas.getWidth() / 2, 'object center.x is in canvas center when the canvas is transformed'); - assert.equal(object.getCenterPoint().y, canvas.getHeight() / 2, 'object center.y is in canvas center when the canvas is transformed'); - - }); - - QUnit.test('centerH', function(assert) { - var object = new fabric.Object(); - object.strokeWidth = 0; - canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; - assert.ok(typeof object.centerH === 'function'); - var oldY = object.top; - - canvas.add(object); - assert.equal(object.centerH(), object, 'should be chainable'); - - assert.equal(object.getCenterPoint().x, canvas.getWidth() / 2); - assert.equal(object.top, oldY, 'object top did not change'); - canvas.setZoom(2); - object.centerH(); - assert.equal(object.getCenterPoint().x, canvas.getWidth() / 2, 'object center.x is in canvas center when the canvas is transformed'); - }); - - QUnit.test('centerV', function(assert) { - var object = new fabric.Object(); - object.strokeWidth = 0; - canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; - assert.ok(typeof object.centerV === 'function'); - var oldX = object.left; - - canvas.add(object); - assert.equal(object.centerV(), object, 'should be chainable'); - assert.equal(object.left, oldX, 'object top did not change'); - assert.equal(object.getCenterPoint().y, canvas.getHeight() / 2); - - canvas.setZoom(2); - object.centerV(); - assert.equal(object.getCenterPoint().y, canvas.getHeight() / 2, 'object center.y is in canvas center when the canvas is transformed'); - }); - - QUnit.test('viewportCenter', function(assert) { - var object = new fabric.Object(); - object.strokeWidth = 0; - canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; - assert.ok(typeof object.viewportCenter === 'function'); - - canvas.add(object); - assert.equal(object.viewportCenter(), object, 'should be chainable'); - - assert.equal(object.getCenterPoint().x, canvas.getWidth() / 2); - assert.equal(object.getCenterPoint().y, canvas.getHeight() / 2); - - canvas.setZoom(2); - object.viewportCenter(); - assert.equal(object.getCenterPoint().x, canvas.getWidth() / (2 * canvas.getZoom())); - assert.equal(object.getCenterPoint().y, canvas.getHeight() / (2 * canvas.getZoom())); - }); - - QUnit.test('viewportCenterH', function(assert) { - var object = new fabric.Object(); - object.strokeWidth = 0; - canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; - assert.ok(typeof object.viewportCenterH === 'function'); - - var oldY = object.top; - canvas.add(object); - assert.equal(object.viewportCenterH(), object, 'should be chainable'); - assert.equal(object.getCenterPoint().x, canvas.getWidth() / 2); - assert.equal(object.top, oldY, 'object top did not change'); - canvas.setZoom(2); - object.viewportCenterH(); - assert.equal(object.getCenterPoint().x, canvas.getWidth() / (2 * canvas.getZoom())); - assert.equal(object.top, oldY, 'object top did not change'); - }); - - QUnit.test('viewportCenterV', function(assert) { - var object = new fabric.Object(); - object.strokeWidth = 0; - canvas.viewportTransform = [1, 0, 0, 1, 0, 0]; - assert.ok(typeof object.viewportCenterV === 'function'); - - var oldX = object.left; - - canvas.add(object); - assert.equal(object.viewportCenterV(), object, 'should be chainable'); - assert.equal(object.getCenterPoint().y, canvas.getHeight() / 2); - assert.equal(object.left, oldX, 'object left did not change'); - canvas.setZoom(2); - object.viewportCenterV(); - assert.equal(object.getCenterPoint().y, canvas.getHeight() / (2 * canvas.getZoom())); - assert.equal(object.left, oldX, 'object left did not change'); - }); - QUnit.test('isDescendantOf', function (assert) { var object = new fabric.Object(); var parent = new fabric.Object(); diff --git a/test/unit/path.js b/test/unit/path.js index 4db3b630b57..d4ed9f8c67a 100644 --- a/test/unit/path.js +++ b/test/unit/path.js @@ -127,11 +127,6 @@ updatePath(path, REFERENCE_PATH_OBJECT.path, true); assert.deepEqual(path.toObject(), REFERENCE_PATH_OBJECT); updatePath(path, REFERENCE_PATH_OBJECT.path, false); - var left = path.left; - var top = path.top; - path.center(); - assert.equal(left, path.left); - assert.equal(top, path.top); var opts = fabric.util.object.clone(REFERENCE_PATH_OBJECT); delete opts.path; path.set(opts);