UNPKG

fabric

Version:

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

105 lines (100 loc) 3.54 kB
import type { TColorArg } from '../../color/typedefs'; import { FILL, STROKE } from '../../constants'; import type { ObjectEvents } from '../../EventTypeDefs'; import type { TAnimation } from '../../util/animation/animate'; import { animate, animateColor } from '../../util/animation/animate'; import type { AnimationOptions, ArrayAnimationOptions, ColorAnimationOptions, ValueAnimationOptions, } from '../../util/animation/types'; import { StackedObject } from './StackedObject'; export abstract class AnimatableObject< EventSpec extends ObjectEvents = ObjectEvents > extends StackedObject<EventSpec> { /** * List of properties to consider for animating colors. * @type String[] */ static colorProperties: string[] = [FILL, STROKE, 'backgroundColor']; /** * Animates object's properties * @param {Record<string, number | number[] | TColorArg>} animatable map of keys and end values * @param {Partial<AnimationOptions<T>>} options * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#animation} * @return {Record<string, TAnimation<T>>} map of animation contexts * * As object — multiple properties * * object.animate({ left: ..., top: ... }); * object.animate({ left: ..., top: ... }, { duration: ... }); */ animate<T extends number | number[] | TColorArg>( animatable: Record<string, T>, options?: Partial<AnimationOptions<T>> ): Record<string, TAnimation<T>> { return Object.entries(animatable).reduce((acc, [key, endValue]) => { acc[key] = this._animate(key, endValue, options); return acc; }, {} as Record<string, TAnimation<T>>); } /** * @private * @param {String} key Property to animate * @param {String} to Value to animate to * @param {Object} [options] Options object */ _animate<T extends number | number[] | TColorArg>( key: string, endValue: T, options: Partial<AnimationOptions<T>> = {} ): TAnimation<T> { const path = key.split('.'); const propIsColor = ( this.constructor as typeof AnimatableObject ).colorProperties.includes(path[path.length - 1]); const { abort, startValue, onChange, onComplete } = options; const animationOptions = { ...options, 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), endValue, abort: abort?.bind(this), onChange: ( value: number | number[] | string, valueProgress: number, durationProgress: number ) => { path.reduce((deep: Record<string, any>, key, index) => { if (index === path.length - 1) { deep[key] = value; } return deep[key]; }, this); onChange && // @ts-expect-error generic callback arg0 is wrong onChange(value, valueProgress, durationProgress); }, onComplete: ( value: number | number[] | string, valueProgress: number, durationProgress: number ) => { this.setCoords(); onComplete && // @ts-expect-error generic callback arg0 is wrong onComplete(value, valueProgress, durationProgress); }, } as AnimationOptions<T>; return ( propIsColor ? animateColor(animationOptions as ColorAnimationOptions) : animate( animationOptions as ValueAnimationOptions | ArrayAnimationOptions ) ) as TAnimation<T>; } }