UNPKG

@proyecto26/animatable-component

Version:
170 lines (169 loc) • 5.33 kB
import { KEYFRAMES } from '../animations'; import { EASING_FUNCTIONS } from '../easing/easing'; /** * Create a new animation. * @param element - The element to animate. * @param context - Animatable context. */ function createAnimation(element, context) { const newKeyFrames = context.keyFrames || (context.animation && KEYFRAMES[context.animation]) || (context.keyFramesData && JSON.parse(context.keyFramesData)) || []; const options = getAnimationOptions(context); const newAnimation = element.animate(newKeyFrames, options); newAnimation.pause(); if (context.currentTime !== undefined) newAnimation.currentTime = context.currentTime; if (context.startTime !== undefined) newAnimation.startTime = context.startTime; return newAnimation; } /** * Clear props of the animatable context. * @param context - Animatable context. * @param options - Keyframe options. */ export function clearPropsWithOptions(context, options) { for (const key in options) { if (options.hasOwnProperty(key)) { if (key === 'id') { context.animateId = undefined; } else { context[key] = undefined; } } } } /** * Load the options of the animation. * @param context - The data of the animation. */ export function getAnimationOptions(context) { const animationOptions = context.options || (context.optionsData && JSON.parse(context.optionsData)) || {}; if (context.delay !== undefined) animationOptions.delay = context.delay; if (context.duration !== undefined) animationOptions.duration = context.duration; if (context.direction !== undefined) animationOptions.direction = context.direction; if (context.composite !== undefined) animationOptions.composite = context.composite; const easingType = (context.easing || animationOptions.easing); animationOptions.easing = EASING_FUNCTIONS[easingType] || easingType; if (context.endDelay !== undefined) animationOptions.endDelay = context.endDelay; if (context.fill !== undefined) animationOptions.fill = context.fill; if (context.animateId !== undefined) animationOptions.id = context.animateId; if (context.iterations !== undefined) animationOptions.iterations = context.iterations; if (context.iterationStart !== undefined) animationOptions.iterationStart = context.iterationStart; if (context.iterationComposite !== undefined) animationOptions.iterationComposite = context.iterationComposite; return animationOptions; } /** * A manager to handle the animations of the Components. */ export class AnimationManager { constructor(initState) { this.animation = null; /** * Emit `onStart` event and update class name with `fromClassName`. */ this.onStartAnimation = () => { this.state.onStart.emit(this.element); if (this.state.fromClassName !== undefined) { this.className = this.element.className; this.element.className = this.state.fromClassName; } }; /** * Emit `onCancel` event and restore class name. */ this.onCancelAnimation = () => { this.state.onCancel.emit(this.element); if (this.state.fromClassName !== undefined && this.className !== undefined) { this.element.className = this.className; } }; /** * Emit `onFinish` event and update class name with `toClassName`. */ this.onFinishAnimation = () => { const { element, state } = this; state.onFinish.emit(element); if (state.toClassName !== undefined) { element.className = state.toClassName; } }; this.state = initState; } get currentAnimation() { return this.animation || this.loadAnimation(); } set currentAnimation(value) { this.animation = value; } loadAnimation() { const { element, state } = this; const newAnimation = createAnimation(element, state); /** * Add listeners */ newAnimation.addEventListener('finish', this.onFinishAnimation); newAnimation.addEventListener('cancel', this.onCancelAnimation); return this.animation = newAnimation; } clearAnimation() { if (this.animation === null) return; this.animation.removeEventListener('finish', this.onFinishAnimation); this.animation.removeEventListener('cancel', this.onCancelAnimation); this.animation = null; } destroyAnimation() { if (this.animation === null) return; const currentAnimation = this.animation; this.clearAnimation(); currentAnimation.cancel(); } /** * Emit start event if playState is not running or playing a new animation. */ playAnimation() { if (this.currentAnimation.playState === 'running' && !this.isUpdatingState) return; /** * Cancel current animation before to create another one */ if (this.isUpdatingState) { this.destroyAnimation(); } this.currentAnimation.play(); this.onStartAnimation(); } setState(element, newState) { this.isUpdatingState = true; this.element = element; this.state = newState; } savedState() { /** * Check if `autoPlay` is enabled to play a new animation and emit the event. */ if (this.state.autoPlay) { this.playAnimation(); } this.isUpdatingState = false; } }