From 1cdfea3297ae32561311596d1b74087cafd0abfe Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Fri, 30 Dec 2022 19:24:11 +0100 Subject: [PATCH 01/24] small removals --- src/shapes/Object/AnimatableObject.ts | 146 ++++---------------------- src/shapes/Object/Object.ts | 1 - src/util/animation/index.ts | 5 - src/util/misc/misc.ts | 8 +- 4 files changed, 24 insertions(+), 136 deletions(-) delete mode 100644 src/util/animation/index.ts diff --git a/src/shapes/Object/AnimatableObject.ts b/src/shapes/Object/AnimatableObject.ts index 6590d666e4d..2211eb68a84 100644 --- a/src/shapes/Object/AnimatableObject.ts +++ b/src/shapes/Object/AnimatableObject.ts @@ -1,13 +1,11 @@ import { TColorArg } from '../../color/color.class'; -import { noop } from '../../constants'; import { ObjectEvents } from '../../EventTypeDefs'; -import { TDegree } from '../../typedefs'; import { animate, animateColor, - AnimationOptions, - ColorAnimationOptions, -} from '../../util/animation'; +} from '../../util/animation/animate'; +import type { AnimationOptions, ColorAnimationOptions } from '../../util/animation/types'; +import { ArrayAnimation } from '../../util/animation/ArrayAnimation'; import type { ColorAnimation } from '../../util/animation/ColorAnimation'; import type { ValueAnimation } from '../../util/animation/ValueAnimation'; import { StackedObject } from './StackedObject'; @@ -19,21 +17,12 @@ type TAnimationOptions = T extends number export abstract class AnimatableObject< EventSpec extends ObjectEvents = ObjectEvents > extends StackedObject { - /** - * Animation duration (in ms) for fx* methods - * @type Number - * @default - */ - FX_DURATION: number; - /** * List of properties to consider for animating colors. * @type String[] */ colorProperties: string[]; - abstract rotate(deg: TDegree): void; - /** * Animates object's properties * @param {String|Object} property Property to animate (if string) or properties to animate (if object) @@ -45,45 +34,16 @@ export abstract class AnimatableObject< * * object.animate({ left: ..., top: ... }); * object.animate({ left: ..., top: ... }, { duration: ... }); - * - * As string — one property - * Supports +=N and -=N for animating N units in a given direction - * - * object.animate('left', ...); - * object.animate('left', ..., { duration: ... }); - * - * Example of +=/-= - * object.animate('right', '-=50'); - * object.animate('top', '+=50', { duration: ... }); */ - animate( - key: string, - toValue: T, - options?: Partial> - ): (ColorAnimation | ValueAnimation)[]; animate( animatable: Record, options?: Partial> - ): (ColorAnimation | ValueAnimation)[]; - animate>( - arg0: S, - arg1: S extends string ? T : Partial>, - arg2?: S extends string ? Partial> : never - ): (ColorAnimation | ValueAnimation)[] { - const animatable = ( - typeof arg0 === 'string' ? { [arg0]: arg1 } : arg0 - ) as Record; - const keys = Object.keys(animatable); - const options = (typeof arg0 === 'string' ? arg2 : arg1) as Partial< - TAnimationOptions - >; - return keys.map((key, index) => + ): (ColorAnimation | ValueAnimation | ArrayAnimation)[] { + return Object.entries(animatable).map(([key, endValue]) => this._animate( key, - animatable[key], - index === keys.length - 1 - ? options - : { ...options, onChange: undefined, onComplete: undefined } + endValue, + options, ) ); } @@ -96,37 +56,21 @@ export abstract class AnimatableObject< */ _animate( key: string, - to: T, + endValue: T, options: Partial> = {} ) { const path = key.split('.'); const propIsColor = this.colorProperties.includes(path[path.length - 1]); - const currentValue = path.reduce((deep: any, key) => deep[key], this); - - if (!propIsColor && typeof to === 'string') { - // check for things like +=50 - // which should animate so that the thing moves by 50 units in the positive direction - to = to.includes('=') - ? currentValue + parseFloat(to.replace('=', '')) - : parseFloat(to); - } - + const { byValue, easing, duration, abort, startValue, onChange, onComplete } = options; const animationOptions = { target: this, - startValue: - options.startValue ?? - // backward compat - (options as any).from ?? - currentValue, - endValue: to, - // `byValue` takes precedence over `endValue` - byValue: - options.byValue ?? - // backward compat - (options as any).by, - easing: options.easing, - duration: options.duration, - abort: options.abort?.bind(this), + // path.reduce... is the current value in case start value isn't provided + startValue: startValue ?? path.reduce((deep: any, key) => deep[key], this), + endValue, + byValue, + easing, + duration, + abort: abort?.bind(this), onChange: ( value: string | number, valueRatio: number, @@ -138,9 +82,9 @@ export abstract class AnimatableObject< } return deep[key]; }, this); - options.onChange && + onChange && // @ts-expect-error generic callback arg0 is wrong - options.onChange(value, valueRatio, durationRatio); + onChange(value, valueRatio, durationRatio); }, onComplete: ( value: string | number, @@ -148,9 +92,9 @@ export abstract class AnimatableObject< durationRatio: number ) => { this.setCoords(); - options.onComplete && + onComplete && // @ts-expect-error generic callback arg0 is wrong - options.onComplete(value, valueRatio, durationRatio); + onComplete(value, valueRatio, durationRatio); }, } as TAnimationOptions; @@ -160,54 +104,4 @@ export abstract class AnimatableObject< return animate(animationOptions as AnimationOptions); } } - - /** - * @private - * @return {Number} angle value - */ - protected _getAngleValueForStraighten() { - const angle = this.angle % 360; - if (angle > 0) { - return Math.round((angle - 1) / 90) * 90; - } - return Math.round(angle / 90) * 90; - } - - /** - * Straightens an object (rotating it from current angle to one of 0, 90, 180, 270, etc. depending on which is closer) - */ - straighten() { - this.rotate(this._getAngleValueForStraighten()); - } - - /** - * Same as {@link straighten} but with animation - * @param {Object} callbacks Object with callback functions - * @param {Function} [callbacks.onComplete] Invoked on completion - * @param {Function} [callbacks.onChange] Invoked on every step of animation - */ - fxStraighten( - callbacks: { - onChange?(value: TDegree): any; - onComplete?(): any; - } = {} - ) { - const onComplete = callbacks.onComplete || noop, - onChange = callbacks.onChange || noop; - - return animate({ - target: this, - startValue: this.angle, - endValue: this._getAngleValueForStraighten(), - duration: this.FX_DURATION, - onChange: (value: TDegree) => { - this.rotate(value); - onChange(value); - }, - onComplete: () => { - this.setCoords(); - onComplete(); - }, - }); - } } diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts index 3c941f96328..b7c1d54ede7 100644 --- a/src/shapes/Object/Object.ts +++ b/src/shapes/Object/Object.ts @@ -2118,7 +2118,6 @@ export const fabricObjectDefaultValues = { clipPath: undefined, inverted: false, absolutePositioned: false, - FX_DURATION: 500, }; Object.assign(FabricObject.prototype, fabricObjectDefaultValues); diff --git a/src/util/animation/index.ts b/src/util/animation/index.ts deleted file mode 100644 index f1fea7f06de..00000000000 --- a/src/util/animation/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './animate'; -export * from './AnimationFrameProvider'; -export * from './AnimationRegistry'; -export * as ease from './easing'; -export * from './types'; diff --git a/src/util/misc/misc.ts b/src/util/misc/misc.ts index 2eb0918460c..66ca09c8b65 100644 --- a/src/util/misc/misc.ts +++ b/src/util/misc/misc.ts @@ -91,13 +91,13 @@ import { } from '../dom_misc'; import { isTransparent } from './isTransparent'; import { mergeClipPaths } from './mergeClipPaths'; +import * as ease from '../animation/easing'; import { animate, animateColor, - ease, - requestAnimFrame, - cancelAnimFrame, -} from '../animation'; +} from '../animation/animate'; +import { requestAnimFrame, +cancelAnimFrame, } from '../animation/AnimationFrameProvider'; import { createClass } from '../lang_class'; import { classRegistry } from '../class_registry'; From 2fe7cbb0956a92498112cee6cf23eb2778e60138 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Fri, 30 Dec 2022 19:25:22 +0100 Subject: [PATCH 02/24] small removals --- src/canvas/static_canvas.class.ts | 2 +- src/shapes/Object/Object.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/canvas/static_canvas.class.ts b/src/canvas/static_canvas.class.ts index ecc98102541..ede834bdfae 100644 --- a/src/canvas/static_canvas.class.ts +++ b/src/canvas/static_canvas.class.ts @@ -22,7 +22,7 @@ import { TToCanvasElementOptions, TValidToObjectMethod, } from '../typedefs'; -import { cancelAnimFrame, requestAnimFrame } from '../util/animation'; +import { cancelAnimFrame, requestAnimFrame } from '../util/animation/AnimationFrameProvider'; import { cleanUpJsdomNode, getElementOffset, diff --git a/src/shapes/Object/Object.ts b/src/shapes/Object/Object.ts index b7c1d54ede7..b8a6a0f5180 100644 --- a/src/shapes/Object/Object.ts +++ b/src/shapes/Object/Object.ts @@ -15,7 +15,7 @@ import type { TCacheCanvasDimensions, } from '../../typedefs'; import { classRegistry } from '../../util/class_registry'; -import { runningAnimations } from '../../util/animation'; +import { runningAnimations } from '../../util/animation/AnimationRegistry'; import { clone } from '../../util/lang_object'; import { capitalize } from '../../util/lang_string'; import { capValue } from '../../util/misc/capValue'; From 47f3cfe9cd8517cfbd16425d9d85e9d31eb6479a Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Fri, 30 Dec 2022 20:02:57 +0100 Subject: [PATCH 03/24] fix tests --- test/unit/animation.js | 35 +++++++++-------------------------- test/unit/canvas_dispose.js | 2 +- test/unit/object.js | 6 +++--- 3 files changed, 13 insertions(+), 30 deletions(-) diff --git a/test/unit/animation.js b/test/unit/animation.js index f980ef483c1..04cb5a882f8 100644 --- a/test/unit/animation.js +++ b/test/unit/animation.js @@ -49,7 +49,7 @@ onComplete: done, }); }); - + QUnit.test('animateColor byValue', function (assert) { var done = assert.async(); fabric.util.animateColor({ @@ -273,7 +273,7 @@ assert.ok(typeof object.animate === 'function'); - object.animate('left', 40); + object.animate({ left: 40 }); assert.ok(true, 'animate without options does not crash'); assert.equal(fabric.runningAnimations.length, 1, 'should have 1 registered animation'); assert.equal(fabric.runningAnimations[0].target, object, 'animation.target should be set'); @@ -290,7 +290,7 @@ var done = assert.async(); var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 43 }); - object.animate('left', '+=40'); + object.animate({ left: object.left + 40 }); assert.ok(true, 'animate without options does not crash'); setTimeout(function() { @@ -304,7 +304,7 @@ var done = assert.async(); var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 43, shadow: { offsetX: 20 } }); - object.animate('shadow.offsetX', 100); + object.animate({ 'shadow.offsetX': 100 }); assert.ok(true, 'animate without options does not crash'); setTimeout(function() { @@ -319,7 +319,7 @@ properties.forEach(function (prop, index) { object.set(prop, 'red'); - object.animate(prop, 'blue'); + object.animate({ [prop]: 'blue' }); assert.ok(true, 'animate without options does not crash'); assert.equal(fabric.runningAnimations.length, index + 1, 'should have 1 registered animation'); assert.equal(findAnimationsByTarget(object).length, index + 1, 'animation.target should be set'); @@ -338,7 +338,7 @@ var done = assert.async(); var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 43 }); - object.animate('left', '-=40'); + object.animate({ left: object.left - 40 }); assert.ok(true, 'animate without options does not crash'); setTimeout(function() { @@ -348,23 +348,6 @@ }, 1000); }); - QUnit.test('animate with object', function(assert) { - var done = assert.async(); - var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 43 }); - - assert.ok(typeof object.animate === 'function'); - - object.animate({ left: 40}); - assert.ok(true, 'animate without options does not crash'); - assert.equal(fabric.runningAnimations.length, 1, 'should have 1 registered animation'); - assert.equal(findAnimationsByTarget(object).length, 1, 'animation.target should be set'); - - setTimeout(function() { - assert.equal(40, Math.round(object.left)); - done(); - }, 1000); - }); - QUnit.test('animate multiple properties', function(assert) { var done = assert.async(); var object = new fabric.Object({ left: 123, top: 124 }); @@ -399,7 +382,7 @@ assert.equal(Math.round(object.get('top')), 1); assert.ok(changedInvocations > 0); - assert.equal(completeInvocations, 1); + assert.equal(completeInvocations, 2, 'the callbacks get call for each animation'); done(); @@ -442,14 +425,14 @@ var done = assert.async(); var object = new fabric.Object({ left: 123, top: 124 }); - var context; + var context; object.animate({ left: 223, top: 224 }, { abort: function() { context = this; return true; } }); - + setTimeout(function() { assert.equal(123, Math.round(object.get('left'))); assert.equal(124, Math.round(object.get('top'))); diff --git a/test/unit/canvas_dispose.js b/test/unit/canvas_dispose.js index 84b9c3822e4..11d4c74d491 100644 --- a/test/unit/canvas_dispose.js +++ b/test/unit/canvas_dispose.js @@ -208,7 +208,7 @@ function testCanvasDisposing() { assert.ok(typeof canvas.dispose === 'function'); assert.ok(typeof canvas.destroy === 'function'); canvas.add(makeRect(), makeRect(), makeRect()); - canvas.item(0).animate('scaleX', 10); + canvas.item(0).animate({ scaleX: 10 }); assert.equal(fabric.runningAnimations.length, 1, 'should have a running animation'); await canvas.dispose(); assert.equal(fabric.runningAnimations.length, 0, 'dispose should clear running animations'); diff --git a/test/unit/object.js b/test/unit/object.js index 85e68f5b276..675cd01e64e 100644 --- a/test/unit/object.js +++ b/test/unit/object.js @@ -511,7 +511,7 @@ assert.equal(canvas.contextContainer.getLineDash().length, 6, 'bailed immediately as array empty'); }); - QUnit.test('straighten', function(assert) { + QUnit.skip('straighten', function(assert) { var object = new fabric.Object({ left: 100, top: 124, width: 210, height: 66 }); assert.ok(typeof object.straighten === 'function'); @@ -540,7 +540,7 @@ assert.equal(object.get('angle'), 270); }); - QUnit.test('fxStraighten', function(assert) { + QUnit.skip('fxStraighten', function(assert) { var done = assert.async(); var object = new fabric.Object({ left: 20, top: 30, width: 40, height: 50, angle: 43 }); @@ -1430,7 +1430,7 @@ QUnit.test('dispose', function (assert) { var object = new fabric.Object({ fill: 'blue', width: 100, height: 100 }); assert.ok(typeof object.dispose === 'function'); - object.animate('fill', 'red'); + object.animate({ fill: 'red' }); const findAnimationsByTarget = target => fabric.runningAnimations.filter(({ target: t }) => target === t); assert.equal(findAnimationsByTarget(object).length, 1, 'runningAnimations should include the animation'); object.dispose(); From 1cdd49d5c31535a50d71b70f06e246b29599403d Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Fri, 30 Dec 2022 20:15:30 +0100 Subject: [PATCH 04/24] added parkinglot --- src/parkinglot/straighten.ts | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/parkinglot/straighten.ts diff --git a/src/parkinglot/straighten.ts b/src/parkinglot/straighten.ts new file mode 100644 index 00000000000..0ab9fec092d --- /dev/null +++ b/src/parkinglot/straighten.ts @@ -0,0 +1,58 @@ +// @ts-nocheck +import { noop } from "../constants"; +import { FabricObject } from "../shapes/Object/FabricObject"; +import { TDegree } from "../typedefs"; +import { animate } from "../util/animation/animate"; + +Object.assign(FabricObject.prototype, { + /** + * @private + * @return {Number} angle value + */ + _getAngleValueForStraighten(this: FabricObject) { + const angle = this.angle % 360; + if (angle > 0) { + return Math.round((angle - 1) / 90) * 90; + } + return Math.round(angle / 90) * 90; + }, + + /** + * Straightens an object (rotating it from current angle to one of 0, 90, 180, 270, etc. depending on which is closer) + */ + straighten(this: FabricObject) { + this.rotate(this._getAngleValueForStraighten()); + }, + + /** + * Same as {@link straighten} but with animation + * @param {Object} callbacks Object with callback functions + * @param {Function} [callbacks.onComplete] Invoked on completion + * @param {Function} [callbacks.onChange] Invoked on every step of animation + */ + fxStraighten( + this: FabricObject, + callbacks: { + onChange?(value: TDegree): any; + onComplete?(): any; + } = {} + ) { + const onComplete = callbacks.onComplete || noop, + onChange = callbacks.onChange || noop; + + return animate({ + target: this, + startValue: this.angle, + endValue: this._getAngleValueForStraighten(), + duration: this.FX_DURATION, + onChange: (value: TDegree) => { + this.rotate(value); + onChange(value); + }, + onComplete: () => { + this.setCoords(); + onComplete(); + }, + }); + } +}); From fe59b48bfff54f546c3a679db08b32c8c1759dbb Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Fri, 30 Dec 2022 21:43:16 +0100 Subject: [PATCH 05/24] renames and color easing as a const --- src/shapes/Object/AnimatableObject.ts | 12 ++++++------ src/util/animation/AnimationBase.ts | 20 ++++++++++---------- src/util/animation/ColorAnimation.ts | 19 +++++++++++-------- src/util/animation/types.ts | 8 ++++---- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/shapes/Object/AnimatableObject.ts b/src/shapes/Object/AnimatableObject.ts index 2211eb68a84..75cd42f4176 100644 --- a/src/shapes/Object/AnimatableObject.ts +++ b/src/shapes/Object/AnimatableObject.ts @@ -73,8 +73,8 @@ export abstract class AnimatableObject< abort: abort?.bind(this), onChange: ( value: string | number, - valueRatio: number, - durationRatio: number + valueProgress: number, + durationProgress: number ) => { path.reduce((deep: Record, key, index) => { if (index === path.length - 1) { @@ -84,17 +84,17 @@ export abstract class AnimatableObject< }, this); onChange && // @ts-expect-error generic callback arg0 is wrong - onChange(value, valueRatio, durationRatio); + onChange(value, valueProgress, durationProgress); }, onComplete: ( value: string | number, - valueRatio: number, - durationRatio: number + valueProgress: number, + durationProgress: number ) => { this.setCoords(); onComplete && // @ts-expect-error generic callback arg0 is wrong - onComplete(value, valueRatio, durationRatio); + onComplete(value, valueProgress, durationProgress); }, } as TAnimationOptions; diff --git a/src/util/animation/AnimationBase.ts b/src/util/animation/AnimationBase.ts index ec358a2ab99..686cc4e6acf 100644 --- a/src/util/animation/AnimationBase.ts +++ b/src/util/animation/AnimationBase.ts @@ -40,11 +40,11 @@ export abstract class AnimationBase< * Time %, or the ratio of `timeElapsed / duration` * @see tick */ - durationRatio = 0; + durationProgress = 0; /** * Value %, or the ratio of `(currentValue - startValue) / (endValue - startValue)` */ - valueRatio = 0; + valueProgress = 0; /** * Current value */ @@ -127,29 +127,29 @@ export abstract class AnimationBase< private tick(t: number) { const durationMs = (t || +new Date()) - this.startTime; const boundDurationMs = Math.min(durationMs, this.duration); - this.durationRatio = boundDurationMs / this.duration; + this.durationProgress = boundDurationMs / this.duration; const { value, changeRatio } = this.calculate(boundDurationMs); this.value = Array.isArray(value) ? (value.slice() as T) : value; - this.valueRatio = changeRatio; + this.valueProgress = changeRatio; if (this._state === 'aborted') { return; - } else if (this._abort(value, this.valueRatio, this.durationRatio)) { + } else if (this._abort(value, this.valueProgress, this.durationProgress)) { this._state = 'aborted'; this.unregister(); } else if (durationMs >= this.duration) { const endValue = this.endValue; - this.durationRatio = this.valueRatio = 1; + this.durationProgress = this.valueProgress = 1; this._onChange( (Array.isArray(endValue) ? endValue.slice() : endValue) as T, - this.valueRatio, - this.durationRatio + this.valueProgress, + this.durationProgress ); this._state = 'completed'; - this._onComplete(endValue, this.valueRatio, this.durationRatio); + this._onComplete(endValue, this.valueProgress, this.durationProgress); this.unregister(); } else { - this._onChange(value, this.valueRatio, this.durationRatio); + this._onChange(value, this.valueProgress, this.durationProgress); requestAnimFrame(this.tick); } } diff --git a/src/util/animation/ColorAnimation.ts b/src/util/animation/ColorAnimation.ts index fe4be4ec021..37385f55e5f 100644 --- a/src/util/animation/ColorAnimation.ts +++ b/src/util/animation/ColorAnimation.ts @@ -1,26 +1,29 @@ import { Color } from '../../color'; import { TRGBAColorSource } from '../../color/color.class'; +import { halfPI } from '../../constants'; import { capValue } from '../misc/capValue'; import { AnimationBase } from './AnimationBase'; -import { ColorAnimationOptions, TOnAnimationChangeCallback } from './types'; +import type { ColorAnimationOptions, TEasingFunction, TOnAnimationChangeCallback } from './types'; + +const defaultColorEasing: TEasingFunction = (timeElapsed, startValue, byValue, duration) => { + const durationProgress = + 1 - Math.cos((timeElapsed / duration) * (halfPI)); + return startValue + byValue * durationProgress; +} const wrapColorCallback = ( callback?: TOnAnimationChangeCallback ) => callback && - ((rgba: TRGBAColorSource, valueRatio: number, durationRatio: number) => - callback(new Color(rgba).toRgba(), valueRatio, durationRatio)); + ((rgba: TRGBAColorSource, valueProgress: number, durationProgress: number) => + callback(new Color(rgba).toRgba(), valueProgress, durationProgress)); export class ColorAnimation extends AnimationBase { constructor({ startValue, endValue, byValue, - easing = (timeElapsed, startValue, byValue, duration) => { - const durationRatio = - 1 - Math.cos((timeElapsed / duration) * (Math.PI / 2)); - return startValue + byValue * durationRatio; - }, + easing = defaultColorEasing, onChange, onComplete, abort, diff --git a/src/util/animation/types.ts b/src/util/animation/types.ts index b5c3af11004..7cfc6bea206 100644 --- a/src/util/animation/types.ts +++ b/src/util/animation/types.ts @@ -5,13 +5,13 @@ export type AnimationState = 'pending' | 'running' | 'completed' | 'aborted'; /** * Callback called every frame * @param {number | number[]} value current value of the animation. - * @param valueRatio ∈ [0, 1], current value / end value. - * @param durationRatio ∈ [0, 1], time passed / duration. + * @param {number} valueProgress ∈ [0, 1], current value / end value. + * @param {number} durationProgress ∈ [0, 1], time passed / duration. */ export type TOnAnimationChangeCallback = ( value: T, - valueRatio: number, - durationRatio: number + valueProgress: number, + durationProgress: number ) => R; /** From a73bf6d9aacd53d73bf365219c4170a0b836ab70 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Fri, 30 Dec 2022 22:02:23 +0100 Subject: [PATCH 06/24] prettier and changelog --- CHANGELOG.md | 4 ++-- src/canvas/static_canvas.class.ts | 5 ++++- src/parkinglot/straighten.ts | 10 ++++----- src/shapes/Object/AnimatableObject.ts | 29 ++++++++++++++++----------- src/util/animation/ColorAnimation.ts | 18 ++++++++++++----- src/util/misc/misc.ts | 9 ++++----- 6 files changed, 45 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cb9bf9ddc9..758a4cece62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Changelog ## [next] - -- refactor(TS): `animate` and `AnimationRegistry` to classes [#8297](https://github.com/fabricjs/fabric.js/pull/8297) +- refactor(): Animation api reduction and semplification [#8547](https://github.com/fabricjs/fabric.js/pull/8547) +- refactor(): `AnimationRegistry` and animations as classes [#8297](https://github.com/fabricjs/fabric.js/pull/8297) BREAKING: - return animation instance from animate instead of a cancel function and remove `findAnimationByXXX` from `AnimationRegistry` - change `animateColor` signature to match `animate`, removed `colorEasing` diff --git a/src/canvas/static_canvas.class.ts b/src/canvas/static_canvas.class.ts index ede834bdfae..b60d17bc1f6 100644 --- a/src/canvas/static_canvas.class.ts +++ b/src/canvas/static_canvas.class.ts @@ -22,7 +22,10 @@ import { TToCanvasElementOptions, TValidToObjectMethod, } from '../typedefs'; -import { cancelAnimFrame, requestAnimFrame } from '../util/animation/AnimationFrameProvider'; +import { + cancelAnimFrame, + requestAnimFrame, +} from '../util/animation/AnimationFrameProvider'; import { cleanUpJsdomNode, getElementOffset, diff --git a/src/parkinglot/straighten.ts b/src/parkinglot/straighten.ts index 0ab9fec092d..352fe68eb2c 100644 --- a/src/parkinglot/straighten.ts +++ b/src/parkinglot/straighten.ts @@ -1,8 +1,8 @@ // @ts-nocheck -import { noop } from "../constants"; -import { FabricObject } from "../shapes/Object/FabricObject"; -import { TDegree } from "../typedefs"; -import { animate } from "../util/animation/animate"; +import { noop } from '../constants'; +import { FabricObject } from '../shapes/Object/FabricObject'; +import { TDegree } from '../typedefs'; +import { animate } from '../util/animation/animate'; Object.assign(FabricObject.prototype, { /** @@ -54,5 +54,5 @@ Object.assign(FabricObject.prototype, { onComplete(); }, }); - } + }, }); diff --git a/src/shapes/Object/AnimatableObject.ts b/src/shapes/Object/AnimatableObject.ts index 75cd42f4176..621893a92ce 100644 --- a/src/shapes/Object/AnimatableObject.ts +++ b/src/shapes/Object/AnimatableObject.ts @@ -1,10 +1,10 @@ import { TColorArg } from '../../color/color.class'; import { ObjectEvents } from '../../EventTypeDefs'; -import { - animate, - animateColor, -} from '../../util/animation/animate'; -import type { AnimationOptions, ColorAnimationOptions } from '../../util/animation/types'; +import { animate, animateColor } from '../../util/animation/animate'; +import type { + AnimationOptions, + ColorAnimationOptions, +} from '../../util/animation/types'; import { ArrayAnimation } from '../../util/animation/ArrayAnimation'; import type { ColorAnimation } from '../../util/animation/ColorAnimation'; import type { ValueAnimation } from '../../util/animation/ValueAnimation'; @@ -40,11 +40,7 @@ export abstract class AnimatableObject< options?: Partial> ): (ColorAnimation | ValueAnimation | ArrayAnimation)[] { return Object.entries(animatable).map(([key, endValue]) => - this._animate( - key, - endValue, - options, - ) + this._animate(key, endValue, options) ); } @@ -61,11 +57,20 @@ export abstract class AnimatableObject< ) { const path = key.split('.'); const propIsColor = this.colorProperties.includes(path[path.length - 1]); - const { byValue, easing, duration, abort, startValue, onChange, onComplete } = options; + const { + byValue, + easing, + duration, + abort, + startValue, + onChange, + onComplete, + } = options; const animationOptions = { target: this, // path.reduce... is the current value in case start value isn't provided - startValue: startValue ?? path.reduce((deep: any, key) => deep[key], this), + startValue: + startValue ?? path.reduce((deep: any, key) => deep[key], this), endValue, byValue, easing, diff --git a/src/util/animation/ColorAnimation.ts b/src/util/animation/ColorAnimation.ts index 37385f55e5f..4a27da601d9 100644 --- a/src/util/animation/ColorAnimation.ts +++ b/src/util/animation/ColorAnimation.ts @@ -3,13 +3,21 @@ import { TRGBAColorSource } from '../../color/color.class'; import { halfPI } from '../../constants'; import { capValue } from '../misc/capValue'; import { AnimationBase } from './AnimationBase'; -import type { ColorAnimationOptions, TEasingFunction, TOnAnimationChangeCallback } from './types'; +import type { + ColorAnimationOptions, + TEasingFunction, + TOnAnimationChangeCallback, +} from './types'; -const defaultColorEasing: TEasingFunction = (timeElapsed, startValue, byValue, duration) => { - const durationProgress = - 1 - Math.cos((timeElapsed / duration) * (halfPI)); +const defaultColorEasing: TEasingFunction = ( + timeElapsed, + startValue, + byValue, + duration +) => { + const durationProgress = 1 - Math.cos((timeElapsed / duration) * halfPI); return startValue + byValue * durationProgress; -} +}; const wrapColorCallback = ( callback?: TOnAnimationChangeCallback diff --git a/src/util/misc/misc.ts b/src/util/misc/misc.ts index 66ca09c8b65..feca1f8a83a 100644 --- a/src/util/misc/misc.ts +++ b/src/util/misc/misc.ts @@ -92,12 +92,11 @@ import { import { isTransparent } from './isTransparent'; import { mergeClipPaths } from './mergeClipPaths'; import * as ease from '../animation/easing'; +import { animate, animateColor } from '../animation/animate'; import { - animate, - animateColor, -} from '../animation/animate'; -import { requestAnimFrame, -cancelAnimFrame, } from '../animation/AnimationFrameProvider'; + requestAnimFrame, + cancelAnimFrame, +} from '../animation/AnimationFrameProvider'; import { createClass } from '../lang_class'; import { classRegistry } from '../class_registry'; From 6b66b476e2405f4eb7ee71c9d491888279a46069 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Fri, 30 Dec 2022 22:12:44 +0100 Subject: [PATCH 07/24] prettier again --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 758a4cece62..70dda9fb36c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ # Changelog ## [next] + - refactor(): Animation api reduction and semplification [#8547](https://github.com/fabricjs/fabric.js/pull/8547) -- refactor(): `AnimationRegistry` and animations as classes [#8297](https://github.com/fabricjs/fabric.js/pull/8297) +- refactor(): `AnimationRegistry` and animations as classes [#8297](https://github.com/fabricjs/fabric.js/pull/8297) BREAKING: - return animation instance from animate instead of a cancel function and remove `findAnimationByXXX` from `AnimationRegistry` - change `animateColor` signature to match `animate`, removed `colorEasing` From 7687271f5baa54beaa3f5c31e0f222c832e54a3d Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Tue, 3 Jan 2023 13:10:17 +0100 Subject: [PATCH 08/24] better comments --- src/util/animation/types.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/animation/types.ts b/src/util/animation/types.ts index 7cfc6bea206..83d9a208959 100644 --- a/src/util/animation/types.ts +++ b/src/util/animation/types.ts @@ -5,8 +5,9 @@ export type AnimationState = 'pending' | 'running' | 'completed' | 'aborted'; /** * Callback called every frame * @param {number | number[]} value current value of the animation. - * @param {number} valueProgress ∈ [0, 1], current value / end value. - * @param {number} durationProgress ∈ [0, 1], time passed / duration. + * @param {number} valueProgress ∈ [0, 1], the current animation progress reflected on value, normalized. + * 0 is the starting value and 1 is the ending value. + * @param {number} durationProgress ∈ [0, 1], the current animation duration normalized to 1. */ export type TOnAnimationChangeCallback = ( value: T, From 2c7b22c466025f236d88c80713a5ac28257b984a Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Tue, 3 Jan 2023 14:08:54 +0100 Subject: [PATCH 09/24] removed by Value --- index.js | 6 ++-- src/shapes/Object/AnimatableObject.ts | 8 ++--- src/util/animation/AnimationBase.ts | 15 ++------ src/util/animation/ArrayAnimation.ts | 3 +- src/util/animation/ColorAnimation.ts | 9 ++--- src/util/animation/ValueAnimation.ts | 11 +++--- src/util/animation/animate.ts | 9 +++-- src/util/animation/types.ts | 40 +++++++++------------ test/unit/animation.js | 50 +++++++-------------------- 9 files changed, 51 insertions(+), 100 deletions(-) diff --git a/index.js b/index.js index f496a21aacf..013f2204d6d 100644 --- a/index.js +++ b/index.js @@ -153,10 +153,12 @@ import { mergeClipPaths } from './src/util/misc/mergeClipPaths'; import { animate, animateColor, - ease, +} from './src/util/animation/animate'; +import * as ease from './src/util/animation/easing'; +import { requestAnimFrame, cancelAnimFrame, -} from './src/util/animation'; +} from './src/util/animation/AnimationFrameProvider'; import { classRegistry } from './src/util/class_registry'; import { removeFromArray } from './src/util/internals/removeFromArray'; import { getRandomInt } from './src/util/internals/getRandomInt'; diff --git a/src/shapes/Object/AnimatableObject.ts b/src/shapes/Object/AnimatableObject.ts index 621893a92ce..014ba1f49a3 100644 --- a/src/shapes/Object/AnimatableObject.ts +++ b/src/shapes/Object/AnimatableObject.ts @@ -2,7 +2,7 @@ import { TColorArg } from '../../color/color.class'; import { ObjectEvents } from '../../EventTypeDefs'; import { animate, animateColor } from '../../util/animation/animate'; import type { - AnimationOptions, + ValueAnimationOptions, ColorAnimationOptions, } from '../../util/animation/types'; import { ArrayAnimation } from '../../util/animation/ArrayAnimation'; @@ -11,7 +11,7 @@ import type { ValueAnimation } from '../../util/animation/ValueAnimation'; import { StackedObject } from './StackedObject'; type TAnimationOptions = T extends number - ? AnimationOptions + ? ValueAnimationOptions : ColorAnimationOptions; export abstract class AnimatableObject< @@ -58,7 +58,6 @@ export abstract class AnimatableObject< const path = key.split('.'); const propIsColor = this.colorProperties.includes(path[path.length - 1]); const { - byValue, easing, duration, abort, @@ -72,7 +71,6 @@ export abstract class AnimatableObject< startValue: startValue ?? path.reduce((deep: any, key) => deep[key], this), endValue, - byValue, easing, duration, abort: abort?.bind(this), @@ -106,7 +104,7 @@ export abstract class AnimatableObject< if (propIsColor) { return animateColor(animationOptions as ColorAnimationOptions); } else { - return animate(animationOptions as AnimationOptions); + return animate(animationOptions as ValueAnimationOptions); } } } diff --git a/src/util/animation/AnimationBase.ts b/src/util/animation/AnimationBase.ts index 686cc4e6acf..deef0f628c6 100644 --- a/src/util/animation/AnimationBase.ts +++ b/src/util/animation/AnimationBase.ts @@ -5,9 +5,7 @@ import { defaultEasing } from './easing'; import { AnimationState, TAbortCallback, - TAnimationBaseOptions, - TAnimationCallbacks, - TAnimationValues, + TBaseAnimationOptions, TEasingFunction, TOnAnimationChangeCallback, } from './types'; @@ -54,12 +52,6 @@ export abstract class AnimationBase< */ private startTime!: number; - /** - * Constructor - * Since both `byValue` and `endValue` are accepted in subclass options - * and are populated with defaults if missing, we defer to `byValue` and - * ignore `endValue` to avoid conflict - */ constructor({ startValue, byValue, @@ -71,8 +63,7 @@ export abstract class AnimationBase< onComplete = noop, abort = defaultAbort, target, - }: Partial & TAnimationCallbacks> & - Required, 'endValue'>>) { + }: TBaseAnimationOptions) { this.tick = this.tick.bind(this); this.duration = duration; @@ -85,7 +76,7 @@ export abstract class AnimationBase< this.target = target; this.startValue = startValue; - this.byValue = byValue; + this.byValue = byValue this.value = this.startValue; this.endValue = this.calculate(this.duration).value; } diff --git a/src/util/animation/ArrayAnimation.ts b/src/util/animation/ArrayAnimation.ts index 8d519715bb5..a8c7bc09081 100644 --- a/src/util/animation/ArrayAnimation.ts +++ b/src/util/animation/ArrayAnimation.ts @@ -5,13 +5,12 @@ export class ArrayAnimation extends AnimationBase { constructor({ startValue = [0], endValue = [100], - byValue = endValue.map((value, i) => value - startValue[i]), ...options }: ArrayAnimationOptions) { super({ ...options, startValue, - byValue, + byValue: endValue.map((value, i) => value - startValue[i]), }); } protected calculate(timeElapsed: number) { diff --git a/src/util/animation/ColorAnimation.ts b/src/util/animation/ColorAnimation.ts index 5cc4e373edd..77293e700ed 100644 --- a/src/util/animation/ColorAnimation.ts +++ b/src/util/animation/ColorAnimation.ts @@ -30,7 +30,6 @@ export class ColorAnimation extends AnimationBase { constructor({ startValue, endValue, - byValue, easing = defaultColorEasing, onChange, onComplete, @@ -42,13 +41,9 @@ export class ColorAnimation extends AnimationBase { super({ ...options, startValue: startColor, - byValue: byValue - ? new Color(byValue) - .setAlpha(Array.isArray(byValue) && byValue[3] ? byValue[3] : 0) - .getSource() - : (endColor.map( + byValue: endColor.map( (value, i) => value - startColor[i] - ) as TRGBAColorSource), + ) as TRGBAColorSource, easing, onChange: wrapColorCallback(onChange), onComplete: wrapColorCallback(onComplete), diff --git a/src/util/animation/ValueAnimation.ts b/src/util/animation/ValueAnimation.ts index e0d329b49c5..17547e7a6f9 100644 --- a/src/util/animation/ValueAnimation.ts +++ b/src/util/animation/ValueAnimation.ts @@ -1,17 +1,16 @@ import { AnimationBase } from './AnimationBase'; -import { AnimationOptions } from './types'; +import { ValueAnimationOptions } from './types'; export class ValueAnimation extends AnimationBase { constructor({ startValue = 0, endValue = 100, - byValue = endValue - startValue, - ...options - }: AnimationOptions) { + ...otherOptions + }: ValueAnimationOptions) { super({ - ...options, + ...otherOptions, startValue, - byValue, + byValue: endValue - startValue, }); } diff --git a/src/util/animation/animate.ts b/src/util/animation/animate.ts index cfcb17fd23e..d9a2272412b 100644 --- a/src/util/animation/animate.ts +++ b/src/util/animation/animate.ts @@ -2,18 +2,17 @@ import { ValueAnimation } from './ValueAnimation'; import { ArrayAnimation } from './ArrayAnimation'; import { ColorAnimation } from './ColorAnimation'; import { - AnimationOptions, + ValueAnimationOptions, ArrayAnimationOptions, ColorAnimationOptions, } from './types'; const isArrayAnimation = ( - options: ArrayAnimationOptions | AnimationOptions + options: ArrayAnimationOptions | ValueAnimationOptions ): options is ArrayAnimationOptions => { return ( Array.isArray(options.startValue) || - Array.isArray(options.endValue) || - Array.isArray(options.byValue) + Array.isArray(options.endValue) ); }; @@ -44,7 +43,7 @@ const isArrayAnimation = ( * */ export const animate = < - T extends AnimationOptions | ArrayAnimationOptions, + T extends ValueAnimationOptions | ArrayAnimationOptions, R extends T extends ArrayAnimationOptions ? ArrayAnimation : ValueAnimation >( options: T diff --git a/src/util/animation/types.ts b/src/util/animation/types.ts index 83d9a208959..e04984ff019 100644 --- a/src/util/animation/types.ts +++ b/src/util/animation/types.ts @@ -68,7 +68,9 @@ export type TAnimationBaseOptions = { /** * The object this animation is being performed on */ - target: unknown; + target?: unknown; + + byValue: T; }; export type TAnimationCallbacks = { @@ -101,34 +103,26 @@ export type TAnimationValues = * @default 0 */ startValue: T; - } & ( - | { - /** - * Ending value(s) - * Ignored if `byValue` exists - * @default 100 - */ - endValue: T; - byValue?: never; - } - | { - /** - * Difference between the start value(s) to the end value(s) - * Overrides `endValue` - * @default [endValue - startValue] - */ - byValue: T; - endValue?: never; - } - ); + /** + * Ending value(s) + * @default 100 + */ + endValue: T; + }; -export type TAnimationOptions = Partial< +export type TBaseAnimationOptions = TAnimationBaseOptions & + Omit, 'endValue'> & + Partial> +; + +export type TAnimationOptions = Partial< + Omit, 'byValue'> & TAnimationValues & TAnimationCallbacks >; -export type AnimationOptions = TAnimationOptions; +export type ValueAnimationOptions = TAnimationOptions; export type ArrayAnimationOptions = TAnimationOptions; diff --git a/test/unit/animation.js b/test/unit/animation.js index 04cb5a882f8..22be4b6348f 100644 --- a/test/unit/animation.js +++ b/test/unit/animation.js @@ -40,24 +40,14 @@ let called = false; fabric.util.animateColor({ startValue: 'red', - endValue: 'magenta', + endValue: 'blue', duration: 96, onChange: function (val, changePerc) { called && assert.ok(changePerc !== 0, 'change percentage'); called = true; }, - onComplete: done, - }); - }); - - QUnit.test('animateColor byValue', function (assert) { - var done = assert.async(); - fabric.util.animateColor({ - startValue: 'red', - byValue: 'blue', - duration: 16, onComplete: function (val, changePerc, timePerc) { - assert.equal(val, 'rgba(255,0,255,1)', 'color is magenta'); + assert.equal(val, 'rgba(0,0,255,1)', 'color is magenta'); assert.equal(changePerc, 1, 'change percentage is 100%'); assert.equal(timePerc, 1, 'time percentage is 100%'); done(); @@ -65,29 +55,14 @@ }); }); - QUnit.test('animateColor byValue with ignored opacity', function (assert) { + QUnit.test('animateColor with opacity', function (assert) { var done = assert.async(); fabric.util.animateColor({ - startValue: 'rgba(255,0,0,0.5)', - byValue: 'rgba(0,0,255,0.5)', - duration: 16, - onComplete: function (val, changePerc, timePerc) { - assert.equal(val, 'rgba(255,0,255,0.5)', 'color is magenta'); - assert.equal(changePerc, 1, 'change percentage is 100%'); - assert.equal(timePerc, 1, 'time percentage is 100%'); - done(); - } - }); - }); - - QUnit.test('animateColor byValue with opacity', function (assert) { - var done = assert.async(); - fabric.util.animateColor({ - startValue: 'red', - byValue: [0, 0, 255, -0.5], + startValue: 'rgba(255, 0, 0, 0.9)', + endValue: 'rgba(0, 0, 255, 0.7)', duration: 16, onComplete: function (val, changePerc, timePerc) { - assert.equal(val, 'rgba(255,0,255,0.5)', 'color is magenta'); + assert.equal(val, 'rgba(0,0,255,0.7)', 'color is animated on all 4 values'); assert.equal(changePerc, 1, 'change percentage is 100%'); assert.equal(timePerc, 1, 'time percentage is 100%'); done(); @@ -95,17 +70,17 @@ }); }); - QUnit.test('animateColor byValue with wrong opacity is ignored', function (assert) { + QUnit.test('animateColor, opacity out of bounds value are ignored', function (assert) { var done = assert.async(); fabric.util.animateColor({ startValue: 'red', - byValue: [0, 0, 255, 0.5], + endValue: [255, 255, 255, 3], duration: 16, onChange: val => { assert.equal(new fabric.Color(val).getAlpha(), 1, 'alpha diff should be ignored') }, onComplete: function (val, changePerc, timePerc) { - assert.equal(val, 'rgba(255,0,255,1)', 'color is magenta'); + assert.equal(val, 'rgba(255,255,255,1)', 'color is normalized to max values'); assert.equal(changePerc, 1, 'change percentage is 100%'); assert.equal(timePerc, 1, 'time percentage is 100%'); done(); @@ -113,15 +88,14 @@ }); }); - QUnit.test('byValue', function (assert) { + QUnit.test('endValue', function (assert) { var done = assert.async(); fabric.util.animate({ - startValue: 0, - byValue: 10, + startValue: 2, endValue: 5, duration: 16, onComplete: function (val, changePerc, timePerc) { - assert.equal(val, 10, 'endValue is ignored'); + assert.equal(val, 5, 'endValue is respected'); assert.equal(changePerc, 1, 'change percentage is 100%'); assert.equal(timePerc, 1, 'time percentage is 100%'); done(); From cd52d717cc07669c047caa338e2402f75bbe7f00 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Tue, 3 Jan 2023 14:25:49 +0100 Subject: [PATCH 10/24] prettier --- index.js | 5 +--- src/shapes/Object/AnimatableObject.ts | 10 ++----- src/util/animation/AnimationBase.ts | 2 +- src/util/animation/ColorAnimation.ts | 4 +-- src/util/animation/animate.ts | 5 +--- src/util/animation/types.ts | 39 ++++++++++++++------------- 6 files changed, 27 insertions(+), 38 deletions(-) diff --git a/index.js b/index.js index 013f2204d6d..12dac219f04 100644 --- a/index.js +++ b/index.js @@ -150,10 +150,7 @@ import { } from './src/util/dom_misc'; import { isTransparent } from './src/util/misc/isTransparent'; import { mergeClipPaths } from './src/util/misc/mergeClipPaths'; -import { - animate, - animateColor, -} from './src/util/animation/animate'; +import { animate, animateColor } from './src/util/animation/animate'; import * as ease from './src/util/animation/easing'; import { requestAnimFrame, diff --git a/src/shapes/Object/AnimatableObject.ts b/src/shapes/Object/AnimatableObject.ts index 014ba1f49a3..6daacfd9094 100644 --- a/src/shapes/Object/AnimatableObject.ts +++ b/src/shapes/Object/AnimatableObject.ts @@ -57,14 +57,8 @@ export abstract class AnimatableObject< ) { const path = key.split('.'); const propIsColor = this.colorProperties.includes(path[path.length - 1]); - const { - easing, - duration, - abort, - startValue, - onChange, - onComplete, - } = options; + const { easing, duration, abort, startValue, onChange, onComplete } = + options; const animationOptions = { target: this, // path.reduce... is the current value in case start value isn't provided diff --git a/src/util/animation/AnimationBase.ts b/src/util/animation/AnimationBase.ts index deef0f628c6..c3b867913b5 100644 --- a/src/util/animation/AnimationBase.ts +++ b/src/util/animation/AnimationBase.ts @@ -76,7 +76,7 @@ export abstract class AnimationBase< this.target = target; this.startValue = startValue; - this.byValue = byValue + this.byValue = byValue; this.value = this.startValue; this.endValue = this.calculate(this.duration).value; } diff --git a/src/util/animation/ColorAnimation.ts b/src/util/animation/ColorAnimation.ts index 77293e700ed..75cbdbc5edb 100644 --- a/src/util/animation/ColorAnimation.ts +++ b/src/util/animation/ColorAnimation.ts @@ -42,8 +42,8 @@ export class ColorAnimation extends AnimationBase { ...options, startValue: startColor, byValue: endColor.map( - (value, i) => value - startColor[i] - ) as TRGBAColorSource, + (value, i) => value - startColor[i] + ) as TRGBAColorSource, easing, onChange: wrapColorCallback(onChange), onComplete: wrapColorCallback(onComplete), diff --git a/src/util/animation/animate.ts b/src/util/animation/animate.ts index d9a2272412b..1bcddb49ad8 100644 --- a/src/util/animation/animate.ts +++ b/src/util/animation/animate.ts @@ -10,10 +10,7 @@ import { const isArrayAnimation = ( options: ArrayAnimationOptions | ValueAnimationOptions ): options is ArrayAnimationOptions => { - return ( - Array.isArray(options.startValue) || - Array.isArray(options.endValue) - ); + return Array.isArray(options.startValue) || Array.isArray(options.endValue); }; /** diff --git a/src/util/animation/types.ts b/src/util/animation/types.ts index e04984ff019..043ba1cb99e 100644 --- a/src/util/animation/types.ts +++ b/src/util/animation/types.ts @@ -96,25 +96,26 @@ export type TAnimationCallbacks = { abort: TAbortCallback; }; -export type TAnimationValues = - | { - /** - * Starting value(s) - * @default 0 - */ - startValue: T; - /** - * Ending value(s) - * @default 100 - */ - endValue: T; - }; - -export type TBaseAnimationOptions = - TAnimationBaseOptions & - Omit, 'endValue'> & - Partial> -; +export type TAnimationValues = { + /** + * Starting value(s) + * @default 0 + */ + startValue: T; + /** + * Ending value(s) + * @default 100 + */ + endValue: T; +}; + +export type TBaseAnimationOptions< + T, + TCallback = T, + TEasing = T +> = TAnimationBaseOptions & + Omit, 'endValue'> & + Partial>; export type TAnimationOptions = Partial< Omit, 'byValue'> & From 64c968e2ac17076f78a5221a2573d5249f202352 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Tue, 3 Jan 2023 14:28:55 +0100 Subject: [PATCH 11/24] removed misc.ts --- src/util/misc/misc.ts | 201 ------------------------------------------ 1 file changed, 201 deletions(-) delete mode 100644 src/util/misc/misc.ts diff --git a/src/util/misc/misc.ts b/src/util/misc/misc.ts deleted file mode 100644 index feca1f8a83a..00000000000 --- a/src/util/misc/misc.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { fabric } from '../../../HEADER'; -import { cos } from './cos'; -import { sin } from './sin'; -import { - rotateVector, - createVector, - calcAngleBetweenVectors, - getUnitVector, - getBisector, -} from './vectors'; -import { degreesToRadians, radiansToDegrees } from './radiansDegreesConversion'; -import { rotatePoint } from './rotatePoint'; -import { getRandomInt, removeFromArray } from '../internals'; -import { projectStrokeOnPoints } from './projectStroke'; -import { - transformPoint, - invertTransform, - composeMatrix, - qrDecompose, - calcDimensionsMatrix, - calcRotateMatrix, - multiplyTransformMatrices, -} from './matrix'; -import { stylesFromArray, stylesToArray, hasStyleChanged } from './textStyles'; -import { clone, extend } from '../lang_object'; -import { - createCanvasElement, - createImage, - copyCanvasElement, - toDataURL, -} from './dom'; -import { toFixed } from './toFixed'; -import { - matrixToSVG, - parsePreserveAspectRatioAttribute, - parseUnit, - getSvgAttributes, -} from './svgParsing'; -import { groupSVGElements } from './groupSVGElements'; -import { findScaleToFit, findScaleToCover } from './findScaleTo'; -import { capValue } from './capValue'; -import { - saveObjectTransform, - resetObjectTransform, - addTransformToObject, - applyTransformToObject, - removeTransformFromObject, - sizeAfterTransform, -} from './objectTransforms'; -import { makeBoundingBoxFromPoints } from './boundingBoxFromPoints'; -import { - calcPlaneChangeMatrix, - sendPointToPlane, - transformPointRelativeToCanvas, - sendObjectToPlane, -} from './planeChange'; -import { escapeXml, graphemeSplit, capitalize } from '../lang_string'; -import { - loadImage, - enlivenObjects, - enlivenObjectEnlivables, -} from './objectEnlive'; -import { pick } from './pick'; -import { - joinPath, - parsePath, - makePathSimpler, - getSmoothPathFromPoints, - getPathSegmentsInfo, - getBoundsOfCurve, - getPointOnPath, - transformPath, - getRegularPolygonPath, -} from '../path'; -import { setStyle } from '../dom_style'; -import { request } from '../dom_request'; -import { - isTouchEvent, - getPointer, - removeListener, - addListener, -} from '../dom_event'; -import { - wrapElement, - getScrollLeftTop, - getElementOffset, - getNodeCanvas, - cleanUpJsdomNode, - makeElementUnselectable, - makeElementSelectable, -} from '../dom_misc'; -import { isTransparent } from './isTransparent'; -import { mergeClipPaths } from './mergeClipPaths'; -import * as ease from '../animation/easing'; -import { animate, animateColor } from '../animation/animate'; -import { - requestAnimFrame, - cancelAnimFrame, -} from '../animation/AnimationFrameProvider'; -import { createClass } from '../lang_class'; -import { classRegistry } from '../class_registry'; - -/** - * @namespace fabric.util - */ -fabric.util = { - cos, - sin, - rotateVector, - createVector, - calcAngleBetweenVectors, - getUnitVector, - getBisector, - degreesToRadians, - radiansToDegrees, - rotatePoint, - // probably we should stop exposing this from the interface - getRandomInt, - removeFromArray, - projectStrokeOnPoints, - // matrix.ts file - transformPoint, - invertTransform, - composeMatrix, - qrDecompose, - calcDimensionsMatrix, - calcRotateMatrix, - multiplyTransformMatrices, - // textStyles.ts file - stylesFromArray, - stylesToArray, - hasStyleChanged, - object: { - clone, - extend, - }, - createCanvasElement, - createImage, - copyCanvasElement, - toDataURL, - toFixed, - matrixToSVG, - parsePreserveAspectRatioAttribute, - groupSVGElements, - parseUnit, - getSvgAttributes, - findScaleToFit, - findScaleToCover, - capValue, - saveObjectTransform, - resetObjectTransform, - addTransformToObject, - applyTransformToObject, - removeTransformFromObject, - makeBoundingBoxFromPoints, - calcPlaneChangeMatrix, - sendPointToPlane, - transformPointRelativeToCanvas, - sendObjectToPlane, - string: { - escapeXml, - graphemeSplit, - capitalize, - }, - loadImage, - enlivenObjects, - enlivenObjectEnlivables, - pick, - joinPath, - parsePath, - makePathSimpler, - getSmoothPathFromPoints, - getPathSegmentsInfo, - getBoundsOfCurve, - getPointOnPath, - transformPath, - getRegularPolygonPath, - request, - setStyle, - isTouchEvent, - getPointer, - removeListener, - addListener, - wrapElement, - getScrollLeftTop, - getElementOffset, - getNodeCanvas, - cleanUpJsdomNode, - makeElementUnselectable, - makeElementSelectable, - isTransparent, - sizeAfterTransform, - mergeClipPaths, - ease, - animateColor, - animate, - requestAnimFrame, - cancelAnimFrame, - createClass, - classRegistry, -}; From dd3c2a0a14577275d7b86b2f3887533968a65dc1 Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Tue, 3 Jan 2023 14:30:45 +0100 Subject: [PATCH 12/24] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38999e29bea..9ebaa9eb204 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## [next] -- refactor(): Animation api reduction and semplification [#8547](https://github.com/fabricjs/fabric.js/pull/8547) +- refactor(): BREAKING: Animation api reduction and semplification (byValue is removed) [#8547](https://github.com/fabricjs/fabric.js/pull/8547) - fix(): regression of canvas migration with pointer and sendPointToPlane [#8563](https://github.com/fabricjs/fabric.js/pull/8563) - chore(TS): Use exports from files to build fabricJS, get rid of HEADER.js [#8549](https://github.com/fabricjs/fabric.js/pull/8549) - chore(): rm `fabric.filterBackend` => `getFilterBackend` [#8487](https://github.com/fabricjs/fabric.js/pull/8487) From 5a72663b79582bd949bb6036cd64f7d46c7f8e1a Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Fri, 6 Jan 2023 23:54:44 +0100 Subject: [PATCH 13/24] Update test/unit/animation.js Co-authored-by: Shachar <34343793+ShaMan123@users.noreply.github.com> --- test/unit/animation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/animation.js b/test/unit/animation.js index 22be4b6348f..66308e03f7c 100644 --- a/test/unit/animation.js +++ b/test/unit/animation.js @@ -47,7 +47,7 @@ called = true; }, onComplete: function (val, changePerc, timePerc) { - assert.equal(val, 'rgba(0,0,255,1)', 'color is magenta'); + assert.equal(val, 'rgba(0,0,255,1)', 'color is blue'); assert.equal(changePerc, 1, 'change percentage is 100%'); assert.equal(timePerc, 1, 'time percentage is 100%'); done(); From c23dce33a4ec052f3a534c31d94612f11873e94f Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 7 Jan 2023 08:46:39 +0200 Subject: [PATCH 14/24] simpler types --- src/util/animation/types.ts | 49 +++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/src/util/animation/types.ts b/src/util/animation/types.ts index 043ba1cb99e..b3e88245d59 100644 --- a/src/util/animation/types.ts +++ b/src/util/animation/types.ts @@ -51,26 +51,24 @@ export type TAnimationBaseOptions = { * Duration of the animation in ms * @default 500 */ - duration?: number; + duration: number; /** * Delay to start the animation in ms * @default 0 */ - delay?: number; + delay: number; /** * Easing function * @default {defaultEasing} */ - easing?: TEasingFunction; + easing: TEasingFunction; /** * The object this animation is being performed on */ - target?: unknown; - - byValue: T; + target: unknown; }; export type TAnimationCallbacks = { @@ -96,31 +94,28 @@ export type TAnimationCallbacks = { abort: TAbortCallback; }; -export type TAnimationValues = { - /** - * Starting value(s) - * @default 0 - */ +export type TBaseAnimationOptions = Partial< + TAnimationBaseOptions & TAnimationCallbacks +> & { startValue: T; - /** - * Ending value(s) - * @default 100 - */ - endValue: T; + byValue: T; }; -export type TBaseAnimationOptions< - T, - TCallback = T, - TEasing = T -> = TAnimationBaseOptions & - Omit, 'endValue'> & - Partial>; - export type TAnimationOptions = Partial< - Omit, 'byValue'> & - TAnimationValues & - TAnimationCallbacks + TAnimationBaseOptions & + TAnimationCallbacks & { + /** + * Starting value(s) + * @default 0 + */ + startValue: T; + + /** + * Ending value(s) + * @default 100 + */ + endValue: T; + } >; export type ValueAnimationOptions = TAnimationOptions; From 0dd3a089dfa41a719a20e9b153817aba91bffd80 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 7 Jan 2023 08:46:41 +0200 Subject: [PATCH 15/24] Update animation.ts --- test/ts/animation.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/ts/animation.ts b/test/ts/animation.ts index be5fcd04400..5b448dfea00 100644 --- a/test/ts/animation.ts +++ b/test/ts/animation.ts @@ -1,5 +1,5 @@ import { IsExact } from 'conditional-type-checks'; -import { animate } from '../../src/util/animation'; +import { animate } from '../../src/util/animation/animate'; function assertStrict(assertTrue: IsExact) { return assertTrue; @@ -9,11 +9,12 @@ animate({ endValue: 3, }); animate({ + // @ts-expect-error `byValue` is not part of options byValue: 2, }); -// @ts-expect-error only one of (`byValue` | `endValue`) is allowed animate({ endValue: 3, + // @ts-expect-error `byValue` is not part of options byValue: 2, }); From 85bd8eef225024910f0e4bc7840c2beed1e81d84 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 7 Jan 2023 09:06:44 +0200 Subject: [PATCH 16/24] fix(): animation types by overload --- src/util/animation/animate.ts | 14 +++++++------- test/ts/animation.ts | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/util/animation/animate.ts b/src/util/animation/animate.ts index 1bcddb49ad8..f9f4ffca478 100644 --- a/src/util/animation/animate.ts +++ b/src/util/animation/animate.ts @@ -39,12 +39,12 @@ const isArrayAnimation = ( * }); * */ -export const animate = < +export function animate(options: ValueAnimationOptions): ValueAnimation; +export function animate(options: ArrayAnimationOptions): ArrayAnimation; +export function animate< T extends ValueAnimationOptions | ArrayAnimationOptions, R extends T extends ArrayAnimationOptions ? ArrayAnimation : ValueAnimation ->( - options: T -): R => { +>(options: T): R { const animation = ( isArrayAnimation(options) ? new ArrayAnimation(options) @@ -52,10 +52,10 @@ export const animate = < ) as R; animation.start(); return animation; -}; +} -export const animateColor = (options: ColorAnimationOptions) => { +export function animateColor(options: ColorAnimationOptions) { const animation = new ColorAnimation(options); animation.start(); return animation; -}; +} diff --git a/test/ts/animation.ts b/test/ts/animation.ts index 5b448dfea00..1c3a47febba 100644 --- a/test/ts/animation.ts +++ b/test/ts/animation.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ import { IsExact } from 'conditional-type-checks'; import { animate } from '../../src/util/animation/animate'; @@ -12,11 +13,15 @@ animate({ // @ts-expect-error `byValue` is not part of options byValue: 2, }); +// @ts-expect-error `byValue` is not part of options animate({ - endValue: 3, - // @ts-expect-error `byValue` is not part of options byValue: 2, + endValue: 3, }); +animate({ + // @ts-expect-error `foo` is not part of options + foo: 'bar', +}) const context = animate({ startValue: 1, @@ -32,7 +37,7 @@ assertStrict(true); const arrayContext = animate({ startValue: [5], - byValue: [1], + endValue: [1], onChange(a, b, c) { assertStrict(true); assertStrict(true); @@ -41,3 +46,27 @@ const arrayContext = animate({ }); assertStrict(true); + +const mixedContextError = animate({ + // @ts-expect-error mixed context + startValue: [5], + endValue: 1, + onChange(a, b, c) { + assertStrict(true); + assertStrict(true); + assertStrict(true); + }, +}); + +const mixedContextError2 = animate({ + startValue: 5, + // @ts-expect-error mixed context + endValue: [1], + onChange(a, b, c) { + assertStrict(true); + assertStrict(true); + assertStrict(true); + }, +}); + + From ff05bc3b3793c71629c2ccdfe8cf2e0fe2dce6be Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 7 Jan 2023 09:18:48 +0200 Subject: [PATCH 17/24] fix(): make types align with logic arrays come first in logic --- src/util/animation/animate.ts | 2 +- test/ts/animation.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/util/animation/animate.ts b/src/util/animation/animate.ts index f9f4ffca478..27be4466537 100644 --- a/src/util/animation/animate.ts +++ b/src/util/animation/animate.ts @@ -39,8 +39,8 @@ const isArrayAnimation = ( * }); * */ -export function animate(options: ValueAnimationOptions): ValueAnimation; export function animate(options: ArrayAnimationOptions): ArrayAnimation; +export function animate(options: ValueAnimationOptions): ValueAnimation; export function animate< T extends ValueAnimationOptions | ArrayAnimationOptions, R extends T extends ArrayAnimationOptions ? ArrayAnimation : ValueAnimation diff --git a/test/ts/animation.ts b/test/ts/animation.ts index 1c3a47febba..cfb40f19b71 100644 --- a/test/ts/animation.ts +++ b/test/ts/animation.ts @@ -48,22 +48,22 @@ const arrayContext = animate({ assertStrict(true); const mixedContextError = animate({ - // @ts-expect-error mixed context startValue: [5], + // @ts-expect-error mixed context endValue: 1, onChange(a, b, c) { - assertStrict(true); + assertStrict(true); assertStrict(true); assertStrict(true); }, }); const mixedContextError2 = animate({ - startValue: 5, // @ts-expect-error mixed context + startValue: 5, endValue: [1], onChange(a, b, c) { - assertStrict(true); + assertStrict(true); assertStrict(true); assertStrict(true); }, From aba89e2d080c7193acd67407ce3796130fda2375 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 7 Jan 2023 10:42:14 +0200 Subject: [PATCH 18/24] future proofing: use freeze instead of concat --- src/util/animation/AnimationBase.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/util/animation/AnimationBase.ts b/src/util/animation/AnimationBase.ts index c3b867913b5..92bb09af5e5 100644 --- a/src/util/animation/AnimationBase.ts +++ b/src/util/animation/AnimationBase.ts @@ -78,7 +78,7 @@ export abstract class AnimationBase< this.startValue = startValue; this.byValue = byValue; this.value = this.startValue; - this.endValue = this.calculate(this.duration).value; + this.endValue = Object.freeze(this.calculate(this.duration).value); } get state() { @@ -120,27 +120,28 @@ export abstract class AnimationBase< const boundDurationMs = Math.min(durationMs, this.duration); this.durationProgress = boundDurationMs / this.duration; const { value, changeRatio } = this.calculate(boundDurationMs); - this.value = Array.isArray(value) ? (value.slice() as T) : value; + this.value = Object.freeze(value); this.valueProgress = changeRatio; if (this._state === 'aborted') { return; - } else if (this._abort(value, this.valueProgress, this.durationProgress)) { + } else if ( + this._abort(this.value, this.valueProgress, this.durationProgress) + ) { this._state = 'aborted'; this.unregister(); } else if (durationMs >= this.duration) { - const endValue = this.endValue; this.durationProgress = this.valueProgress = 1; - this._onChange( - (Array.isArray(endValue) ? endValue.slice() : endValue) as T, + this._onChange(this.endValue, this.valueProgress, this.durationProgress); + this._state = 'completed'; + this._onComplete( + this.endValue, this.valueProgress, this.durationProgress ); - this._state = 'completed'; - this._onComplete(endValue, this.valueProgress, this.durationProgress); this.unregister(); } else { - this._onChange(value, this.valueProgress, this.durationProgress); + this._onChange(this.value, this.valueProgress, this.durationProgress); requestAnimFrame(this.tick); } } From b50b98f7a4fe568f50f3f4777ca87593bb28a073 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 7 Jan 2023 10:57:52 +0200 Subject: [PATCH 19/24] Update AnimationBase.ts --- src/util/animation/AnimationBase.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/animation/AnimationBase.ts b/src/util/animation/AnimationBase.ts index 92bb09af5e5..5cbcbfccff6 100644 --- a/src/util/animation/AnimationBase.ts +++ b/src/util/animation/AnimationBase.ts @@ -16,10 +16,11 @@ export abstract class AnimationBase< T extends number | number[] = number | number[] > { readonly startValue: T; - readonly byValue: T; readonly endValue: T; readonly duration: number; readonly delay: number; + + protected readonly byValue: T; protected readonly easing: TEasingFunction; private readonly _onStart: VoidFunction; From 0c0e746030ef87b6c53e2570558bf6dacba68fa5 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 7 Jan 2023 11:05:52 +0200 Subject: [PATCH 20/24] Update animation.js --- test/unit/animation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/animation.js b/test/unit/animation.js index 66308e03f7c..01334ee9d61 100644 --- a/test/unit/animation.js +++ b/test/unit/animation.js @@ -375,7 +375,7 @@ onChange: function(currentValue) { assert.equal(fabric.runningAnimations.length, 1, 'runningAnimations should not be empty'); assert.ok(Array.isArray(currentValue), 'should be array'); - assert.ok(fabric.runningAnimations[0].value !== currentValue, 'should not share array'); + assert.ok(Object.isFrozen(fabric.runningAnimations[0].value), 'should be frozen'); assert.deepEqual(fabric.runningAnimations[0].value, currentValue); assert.equal(currentValue.length, 3); currentValue.forEach(function(v) { From ef302902ad77884d22ba585a14bdd6dec7fa5320 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sat, 7 Jan 2023 11:07:00 +0200 Subject: [PATCH 21/24] Update animation.js --- test/unit/animation.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/animation.js b/test/unit/animation.js index 01334ee9d61..21e2f35e3df 100644 --- a/test/unit/animation.js +++ b/test/unit/animation.js @@ -386,7 +386,8 @@ currentValue[0] = 200; run = true; }, - onComplete: function(endValue) { + onComplete: function (endValue) { + assert.ok(Object.isFrozen(endValue), 'should be frozen'); assert.equal(endValue.length, 3); assert.deepEqual(endValue, [2, 4, 6]); assert.equal(run, true, 'something run'); From 25605ce788a5fa2c2a24e7893258c4feb64797b2 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 8 Jan 2023 11:15:34 +0200 Subject: [PATCH 22/24] fix(): `changeRatio` for opacity anim --- src/util/animation/ColorAnimation.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/util/animation/ColorAnimation.ts b/src/util/animation/ColorAnimation.ts index 75cbdbc5edb..d27d495e3cf 100644 --- a/src/util/animation/ColorAnimation.ts +++ b/src/util/animation/ColorAnimation.ts @@ -54,12 +54,15 @@ export class ColorAnimation extends AnimationBase { const [r, g, b, a] = this.startValue.map((value, i) => this.easing(timeElapsed, value, this.byValue[i], this.duration, i) ) as TRGBAColorSource; - const rgb = [r, g, b].map(Math.round); + const value = [ + ...[r, g, b].map(Math.round), + capValue(0, a, 1), + ] as TRGBAColorSource; return { - value: [...rgb, capValue(0, a, 1)] as TRGBAColorSource, + value, changeRatio: // to correctly calculate the change ratio we must find a changed value - rgb + value .map((p, i) => this.byValue[i] !== 0 ? Math.abs((p - this.startValue[i]) / this.byValue[i]) From 1541b5a10b2a86cb67e896e8a66a50606798d891 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 8 Jan 2023 11:19:10 +0200 Subject: [PATCH 23/24] rename: changeRatio => valueProgress --- src/util/animation/AnimationBase.ts | 6 +++--- src/util/animation/ArrayAnimation.ts | 4 +++- src/util/animation/ColorAnimation.ts | 2 +- src/util/animation/ValueAnimation.ts | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/util/animation/AnimationBase.ts b/src/util/animation/AnimationBase.ts index 5cbcbfccff6..c83ea2615ef 100644 --- a/src/util/animation/AnimationBase.ts +++ b/src/util/animation/AnimationBase.ts @@ -93,7 +93,7 @@ export abstract class AnimationBase< */ protected abstract calculate(timeElapsed: number): { value: T; - changeRatio: number; + valueProgress: number; }; start() { @@ -120,9 +120,9 @@ export abstract class AnimationBase< const durationMs = (t || +new Date()) - this.startTime; const boundDurationMs = Math.min(durationMs, this.duration); this.durationProgress = boundDurationMs / this.duration; - const { value, changeRatio } = this.calculate(boundDurationMs); + const { value, valueProgress } = this.calculate(boundDurationMs); this.value = Object.freeze(value); - this.valueProgress = changeRatio; + this.valueProgress = valueProgress; if (this._state === 'aborted') { return; diff --git a/src/util/animation/ArrayAnimation.ts b/src/util/animation/ArrayAnimation.ts index a8c7bc09081..57470f8a175 100644 --- a/src/util/animation/ArrayAnimation.ts +++ b/src/util/animation/ArrayAnimation.ts @@ -19,7 +19,9 @@ export class ArrayAnimation extends AnimationBase { ); return { value: values, - changeRatio: Math.abs((values[0] - this.startValue[0]) / this.byValue[0]), + valueProgress: Math.abs( + (values[0] - this.startValue[0]) / this.byValue[0] + ), }; } } diff --git a/src/util/animation/ColorAnimation.ts b/src/util/animation/ColorAnimation.ts index d27d495e3cf..2b7f3c0a0e7 100644 --- a/src/util/animation/ColorAnimation.ts +++ b/src/util/animation/ColorAnimation.ts @@ -60,7 +60,7 @@ export class ColorAnimation extends AnimationBase { ] as TRGBAColorSource; return { value, - changeRatio: + valueProgress: // to correctly calculate the change ratio we must find a changed value value .map((p, i) => diff --git a/src/util/animation/ValueAnimation.ts b/src/util/animation/ValueAnimation.ts index 17547e7a6f9..45fd25de64c 100644 --- a/src/util/animation/ValueAnimation.ts +++ b/src/util/animation/ValueAnimation.ts @@ -23,7 +23,7 @@ export class ValueAnimation extends AnimationBase { ); return { value, - changeRatio: Math.abs((value - this.startValue) / this.byValue), + valueProgress: Math.abs((value - this.startValue) / this.byValue), }; } } From 5eef2438fbe06088355b8db749ce09b3667dd4d0 Mon Sep 17 00:00:00 2001 From: ShaMan123 Date: Sun, 8 Jan 2023 11:56:04 +0200 Subject: [PATCH 24/24] Update animation.js --- test/unit/animation.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/test/unit/animation.js b/test/unit/animation.js index 21e2f35e3df..e2f1a534f5f 100644 --- a/test/unit/animation.js +++ b/test/unit/animation.js @@ -88,6 +88,28 @@ }); }); + QUnit.test('animateColor opacity only', function (assert) { + var done = assert.async(); + let called = false; + fabric.util.animateColor({ + startValue: 'rgba(255, 0, 0, 0.9)', + endValue: 'rgba(255, 0, 0, 0.7)', + duration: 96, + onChange: function (val, changePerc) { + const alpha = new fabric.Color(val).getAlpha(); + assert.equal(changePerc, (0.9 - alpha) / (0.9 - 0.7), 'valueProgress should match'); + called = true; + }, + onComplete: function (val, changePerc, timePerc) { + assert.equal(val, 'rgba(255,0,0,0.7)', 'color is animated on all 4 values'); + assert.equal(changePerc, 1, 'change percentage is 100%'); + assert.equal(timePerc, 1, 'time percentage is 100%'); + assert.ok(called); + done(); + } + }); + }); + QUnit.test('endValue', function (assert) { var done = assert.async(); fabric.util.animate({ @@ -370,9 +392,8 @@ fabric.util.animate({ startValue: [1, 2, 3], endValue: [2, 4, 6], - byValue: [1, 2, 3], duration: 96, - onChange: function(currentValue) { + onChange: function(currentValue, valueProgress) { assert.equal(fabric.runningAnimations.length, 1, 'runningAnimations should not be empty'); assert.ok(Array.isArray(currentValue), 'should be array'); assert.ok(Object.isFrozen(fabric.runningAnimations[0].value), 'should be frozen'); @@ -381,6 +402,7 @@ currentValue.forEach(function(v) { assert.ok(v > 0, 'confirm values are not invalid numbers'); }) + assert.equal(valueProgress, currentValue[0] - 1, 'should match'); // Make sure mutations are not kept assert.ok(currentValue[0] <= 2, 'mutating callback values must not persist'); currentValue[0] = 200;