Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [next]

- refactor(): create Filler base class for Pattern/Gradient [#8947](https://github.com/fabricjs/fabric.js/pull/8947)
- ci(): automate PR changelog [#8938](https://github.com/fabricjs/fabric.js/pull/8938)
- chore(): move canvas click handler to TextManager [#8939](https://github.com/fabricjs/fabric.js/pull/8939)

Expand Down
26 changes: 6 additions & 20 deletions src/Pattern/Pattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,22 @@ import type { Abortable, TCrossOrigin, TMat2D, TSize } from '../typedefs';
import { ifNaN } from '../util/internals';
import { uid } from '../util/internals/uid';
import { loadImage } from '../util/misc/objectEnlive';
import { pick } from '../util/misc/pick';
import { toFixed } from '../util/misc/toFixed';
import { classRegistry } from '../ClassRegistry';
import type {
PatternRepeat,
PatternOptions,
SerializedPatternOptions,
} from './types';
import { Filler } from '../fillers/Filler';

/**
* @see {@link http://fabricjs.com/patterns demo}
* @see {@link http://fabricjs.com/dynamic-patterns demo}
*/
export class Pattern {
export class Pattern extends Filler<CanvasPattern> {
/**
* Legacy identifier of the class. Prefer using this.constructor.name 'Pattern'
* or utils like isPattern
* Legacy identifier of the class. Prefer using this.constructor.name or `instanceof`
* Will be removed in fabric 7 or 8.
* @TODO add sustainable warning message
* @type string
Expand All @@ -39,20 +38,6 @@ export class Pattern {
*/
repeat: PatternRepeat = 'repeat';

/**
* Pattern horizontal offset from object's left/top corner
* @type Number
* @default
*/
offsetX = 0;

/**
* Pattern vertical offset from object's left/top corner
* @type Number
* @default
*/
offsetY = 0;

/**
* @type TCrossOrigin
* @default
Expand Down Expand Up @@ -90,6 +75,7 @@ export class Pattern {
* @param {option.source} [source] the pattern source, eventually empty or a drawable
*/
constructor(options: PatternOptions = {}) {
super();
this.id = uid();
Object.assign(this, options);
}
Expand Down Expand Up @@ -144,10 +130,10 @@ export class Pattern {
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {object} Object representation of a pattern instance
*/
toObject(propertiesToInclude: string[] = []): Record<string, any> {
toObject<T extends keyof this>(propertiesToInclude?: T[]) {
const { repeat, crossOrigin } = this;
return {
...pick(this, propertiesToInclude as (keyof this)[]),
...super.toObject(propertiesToInclude),
type: 'pattern',
source: this.sourceToString(),
repeat,
Expand Down
37 changes: 20 additions & 17 deletions src/canvas/StaticCanvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { CanvasEvents, StaticCanvasEvents } from '../EventTypeDefs';
import type { Gradient } from '../gradient/Gradient';
import { createCollectionMixin } from '../Collection';
import { CommonMethods } from '../CommonMethods';
import type { Pattern } from '../Pattern';
import { Pattern } from '../Pattern';
import { Point } from '../Point';
import type { BaseFabricObject as FabricObject } from '../EventTypeDefs';
import type { TCachedFabricObject } from '../shapes/Object/Object';
Expand All @@ -15,13 +15,13 @@ import type {
Constructor,
TCornerPoint,
TDataUrlOptions,
TFiller,
TMat2D,
TSize,
TSVGReviver,
TToCanvasElementOptions,
TValidToObjectMethod,
} from '../typedefs';
import type { TFiller } from '../fillers/typedefs';
import {
cancelAnimFrame,
requestAnimFrame,
Expand All @@ -38,12 +38,8 @@ import {
import { pick } from '../util/misc/pick';
import { matrixToSVG } from '../util/misc/svgParsing';
import { toFixed } from '../util/misc/toFixed';
import {
isCollection,
isFiller,
isPattern,
isTextObject,
} from '../util/typeAssertions';
import { isCollection, isTextObject } from '../util/typeAssertions';
import { isFiller } from '../fillers/Filler';

export type TCanvasSizeOptions = {
backstoreOnly?: boolean;
Expand Down Expand Up @@ -1405,20 +1401,27 @@ export class StaticCanvas<
additionalTransform = shouldInvert
? matrixToSVG(invertTransform(this.viewportTransform))
: '';
const { width = finalWidth, height = finalHeight } =
filler instanceof Pattern
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change, importing Pattern

? {
width:
repeat === 'repeat-y' || repeat === 'no-repeat'
? filler.source.width
: undefined,
height:
repeat === 'repeat-x' || repeat === 'no-repeat'
? filler.source.height
: undefined,
}
: {};
markup.push(
`<rect transform="${additionalTransform} translate(${finalWidth / 2},${
finalHeight / 2
})" x="${filler.offsetX - finalWidth / 2}" y="${
filler.offsetY - finalHeight / 2
}" width="${
(repeat === 'repeat-y' || repeat === 'no-repeat') && isPattern(filler)
? filler.source.width
: finalWidth
}" height="${
(repeat === 'repeat-x' || repeat === 'no-repeat') && isPattern(filler)
? filler.source.height
: finalHeight
}" fill="url(#SVGID_${filler.id})"></rect>\n`
}" width="${width}" height="${height}" fill="url(#SVGID_${
filler.id
})"></rect>\n`
);
} else {
markup.push(
Expand Down
61 changes: 61 additions & 0 deletions src/fillers/Filler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { config } from '../config';
import type { Point } from '../Point';
import type { TSize } from '../typedefs';
import type { TFiller } from './typedefs';
import { pick } from '../util/misc/pick';
import { toFixed } from '../util/misc/toFixed';

export type TFillerAction = 'stroke' | 'fill';

export type TFillerRenderingOptions = {
action: TFillerAction;
size: TSize;
offset: Point;
noTransform?: boolean;
};

export const isFiller = (
filler: TFiller | string | null
): filler is TFiller => {
return !!filler && filler instanceof Filler;
};

export abstract class Filler<T extends CanvasPattern | CanvasGradient> {
/**
* horizontal offset from object's left/top corner
* @type Number
* @default
*/
offsetX = 0;

/**
* vertical offset from object's left/top corner
* @type Number
* @default
*/
offsetY = 0;

protected abstract toLive(
ctx: CanvasRenderingContext2D,
options: TFillerRenderingOptions
): T | null;

protected prepare(
ctx: CanvasRenderingContext2D,
options: TFillerRenderingOptions
): Point | void {
ctx[`${options.action}Style`] = this.toLive(ctx, options) || '';
}

toObject<T extends keyof this>(propertiesToInclude?: T[]) {
return {
...pick(this, propertiesToInclude),
offsetX: toFixed(this.offsetX, config.NUM_FRACTION_DIGITS),
offsetY: toFixed(this.offsetY, config.NUM_FRACTION_DIGITS),
};
}

toJSON() {
return this.toObject();
}
}
4 changes: 4 additions & 0 deletions src/fillers/typedefs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { Gradient } from '../gradient/Gradient';
import type { Pattern } from '../Pattern';

export type TFiller = Gradient<'linear'> | Gradient<'radial'> | Pattern;
23 changes: 5 additions & 18 deletions src/gradient/Gradient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { FabricObject } from '../shapes/Object/FabricObject';
import { FabricObject as BaseFabricObject } from '../shapes/Object/Object';
import type { TMat2D } from '../typedefs';
import { uid } from '../util/internals/uid';
import { pick } from '../util/misc/pick';
import { matrixToSVG } from '../util/misc/svgParsing';
import { linearDefaultCoords, radialDefaultCoords } from './constants';
import {
Expand All @@ -24,6 +23,7 @@ import type {
SVGOptions,
} from './typedefs';
import { classRegistry } from '../ClassRegistry';
import { Filler } from '../fillers/Filler';

/**
* Gradient class
Expand All @@ -33,21 +33,7 @@ import { classRegistry } from '../ClassRegistry';
export class Gradient<
S,
T extends GradientType = S extends GradientType ? S : 'linear'
> {
/**
* Horizontal offset for aligning gradients coming from SVG when outside pathgroups
* @type Number
* @default 0
*/
declare offsetX: number;

/**
* Vertical offset for aligning gradients coming from SVG when outside pathgroups
* @type Number
* @default 0
*/
declare offsetY: number;

> extends Filler<CanvasGradient> {
/**
* A transform matrix to apply to the gradient before painting.
* Imported from svg gradients, is not applied with the current transform in the center.
Expand Down Expand Up @@ -111,6 +97,7 @@ export class Gradient<
gradientTransform = null,
id,
}: GradientOptions<T>) {
super();
this.id = id ? `${id}_${uid()}` : uid();
this.type = type;
this.gradientUnits = gradientUnits;
Expand Down Expand Up @@ -150,9 +137,9 @@ export class Gradient<
* @param {string[]} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {object}
*/
toObject(propertiesToInclude?: (keyof this | string)[]) {
toObject<T extends keyof this>(propertiesToInclude?: T[]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not attached to this at all

return {
...pick(this, propertiesToInclude),
...super.toObject(propertiesToInclude),
type: this.type,
coords: this.coords,
colorStops: this.colorStops,
Expand Down
3 changes: 2 additions & 1 deletion src/shapes/IText/IText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
keysMap,
keysMapRtl,
} from './constants';
import type { AssertKeys, TFiller } from '../../typedefs';
import type { AssertKeys } from '../../typedefs';
import type { TFiller } from '../../fillers/typedefs';
import { classRegistry } from '../../ClassRegistry';
import type { SerializedTextProps, TextProps } from '../Text/Text';

Expand Down
2 changes: 1 addition & 1 deletion src/shapes/Line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { TClassProperties } from '../typedefs';
import { classRegistry } from '../ClassRegistry';
import { FabricObject, cacheProperties } from './Object/FabricObject';
import { Point } from '../Point';
import { isFiller } from '../util/typeAssertions';
import { isFiller } from '../fillers/Filler';
import type {
FabricObjectProps,
SerializedObjectProps,
Expand Down
17 changes: 5 additions & 12 deletions src/shapes/Object/Object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { Point } from '../../Point';
import { Shadow } from '../../Shadow';
import type {
TDegree,
TFiller,
TSize,
TCacheCanvasDimensions,
Abortable,
} from '../../typedefs';
import type { TFiller } from '../../fillers/typedefs';
import { classRegistry } from '../../ClassRegistry';
import { runningAnimations } from '../../util/animation/AnimationRegistry';
import { cloneDeep } from '../../util/internals/cloneDeep';
Expand All @@ -28,11 +28,8 @@ import { pick, pickBy } from '../../util/misc/pick';
import { toFixed } from '../../util/misc/toFixed';
import type { Group } from '../Group';
import { StaticCanvas } from '../../canvas/StaticCanvas';
import {
isFiller,
isSerializableFiller,
isTextObject,
} from '../../util/typeAssertions';
import { isTextObject } from '../../util/typeAssertions';
import { isFiller } from '../../fillers/Filler';
import type { Image } from '../Image';
import {
cacheProperties,
Expand Down Expand Up @@ -516,12 +513,8 @@ 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: isSerializableFiller(this.fill)
? this.fill.toObject()
: this.fill,
stroke: isSerializableFiller(this.stroke)
? this.stroke.toObject()
: this.stroke,
fill: isFiller(this.fill) ? this.fill.toObject() : this.fill,
stroke: isFiller(this.stroke) ? this.stroke.toObject() : this.stroke,
strokeWidth: toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
strokeDashArray: this.strokeDashArray
? this.strokeDashArray.concat()
Expand Down
2 changes: 1 addition & 1 deletion src/shapes/Object/types/FillStrokeProps.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { TFiller } from '../../../typedefs';
import type { TFiller } from '../../../fillers/typedefs';

export interface FillStrokeProps {
/**
Expand Down
2 changes: 1 addition & 1 deletion src/shapes/Object/types/ObjectProps.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Shadow } from '../../../Shadow';
import type { Canvas } from '../../../canvas/Canvas';
import type { StaticCanvas } from '../../../canvas/StaticCanvas';
import type { TFiller } from '../../../typedefs';
import type { TFiller } from '../../../fillers/typedefs';
import type { FabricObject } from '../Object';
import type {
ClipPathProps,
Expand Down
7 changes: 2 additions & 5 deletions src/shapes/Text/Text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ import { StyledText } from './StyledText';
import { SHARED_ATTRIBUTES } from '../../parser/attributes';
import { parseAttributes } from '../../parser/parseAttributes';
import type { Point } from '../../Point';
import type {
TCacheCanvasDimensions,
TClassProperties,
TFiller,
} from '../../typedefs';
import type { TCacheCanvasDimensions, TClassProperties } from '../../typedefs';
import type { TFiller } from '../../fillers/typedefs';
import { classRegistry } from '../../ClassRegistry';
import { graphemeSplit } from '../../util/lang_string';
import { createCanvasElement } from '../../util/misc/dom';
Expand Down
4 changes: 0 additions & 4 deletions src/typedefs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// https://www.typescriptlang.org/docs/handbook/utility-types.html
import type { BaseFabricObject } from './EventTypeDefs';
import type { Gradient } from './gradient/Gradient';
import type { Pattern } from './Pattern';
import type { XY, Point } from './Point';

interface NominalTag<T> {
Expand Down Expand Up @@ -29,8 +27,6 @@ export type TAxis = 'x' | 'y';

export type TAxisKey<T extends string> = `${T}${Capitalize<TAxis>}`;

export type TFiller = Gradient<'linear'> | Gradient<'radial'> | Pattern;

export type TSize = {
width: number;
height: number;
Expand Down
3 changes: 2 additions & 1 deletion src/util/misc/objectEnlive.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { noop } from '../../constants';
import type { Pattern } from '../../Pattern';
import type { FabricObject } from '../../shapes/Object/FabricObject';
import type { Abortable, TCrossOrigin, TFiller } from '../../typedefs';
import type { Abortable, TCrossOrigin } from '../../typedefs';
import type { TFiller } from '../../fillers/typedefs';
import { createImage } from './dom';
import { classRegistry } from '../../ClassRegistry';

Expand Down
Loading