@proyecto26/animatable-component
Version:
Animate once, use Everywhere! 💫
513 lines (506 loc) • 19.8 kB
JavaScript
import { r as registerInstance, c as createEvent, h, g as getElement, H as Host } from './index-d6bfca92.js';
import { K as KEYFRAMES, a as EASING_FUNCTIONS, g as getKeyFramesByAnimation } from './index-9a8fe12c.js';
/**
* Get element to animate
*/
const getElementToAnimate = (parent) => {
return parent.shadowRoot !== null ? parent : (parent.firstElementChild
|| (parent.children.length && parent.children[0])
|| parent);
};
/**
* 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.
*/
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.
*/
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.
*/
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;
}
}
const AnimatableComponent = class {
constructor(hostRef) {
registerInstance(this, hostRef);
this.onStart = createEvent(this, "start", 3);
this.onFinish = createEvent(this, "finish", 3);
this.onCancel = createEvent(this, "cancel", 3);
/**
* Animation manager for Animatable
*/
this.manager = null;
/**
* Start the animation when the component is mounted.
*/
this.autoPlay = false;
}
get element() {
return getElementToAnimate(this.el);
}
animationDidChangeHandler(animation) {
this.keyFrames = getKeyFramesByAnimation(animation);
}
/**
* Get keyFrames of the animation from string data.
* @param text - The string with the keyFrames of the animation.
*/
keyFramesDidChangeHandler(text) {
if (text !== undefined)
this.keyFrames = JSON.parse(text);
}
/**
* Get options of the animation from string data.
* @param text - The string with the options of the animation.
*/
optionsDidChangeHandler(options) {
clearPropsWithOptions(this, options);
}
/**
* Get options of the animation from string data.
* @param text - The string with the options of the animation.
*/
optionsDataDidChangeHandler(text) {
if (text !== undefined)
this.options = JSON.parse(text);
}
setCurrenTime(newValue) {
this.manager.currentAnimation.currentTime = newValue;
}
/**
* Returns the current time value of the animation in milliseconds, whether running or paused.
*/
async getCurrentTime() {
return Promise.resolve(this.manager.currentAnimation.currentTime);
}
setStartTime(newValue) {
this.manager.currentAnimation.startTime = newValue;
}
/**
* Returns the scheduled time when an animation's playback should begin.
*/
async getStartTime() {
return Promise.resolve(this.manager.currentAnimation.startTime);
}
/**
* Indicates whether the animation is currently waiting
* for an asynchronous operation such as initiating playback
* or pausing a running animation.
*/
async getPending() {
return Promise.resolve(this.manager.currentAnimation.pending);
}
setPlaybackRate(newValue) {
this.manager.currentAnimation.playbackRate = newValue;
}
/**
* Returns the playback rate of the animation.
*/
async getPlaybackRate() {
return Promise.resolve(this.manager.currentAnimation.playbackRate);
}
/**
* Returns an enumerated value describing the playback state of an animation.
*/
async getPlayState() {
return Promise.resolve(this.manager.currentAnimation.playState);
}
/**
* Clears all `KeyframeEffects` caused by this animation and aborts its playback.
*/
async cancel() {
this.manager.currentAnimation.cancel();
}
/**
* Sets the current playback time to the end of the animation
* corresponding to the current playback direction.
*/
async finish() {
this.manager.currentAnimation.finish();
}
/**
* Suspends playback of the animation.
*/
async pause() {
this.manager.currentAnimation.pause();
}
/**
* Starts or resumes playing of an animation.
*/
async play() {
this.manager.playAnimation();
}
/**
* Reverses the playback direction, meaning the animation ends at its beginning.
*/
async reverse() {
this.manager.currentAnimation.reverse();
}
/**
* Clear the current animation
*/
async clear() {
this.manager.clearAnimation();
}
/**
* Destroy the current animation
*/
async destroy() {
if (this.manager !== null) {
this.manager.destroyAnimation();
}
}
/**
* Initialize manager
*/
connectedCallback() {
this.manager = new AnimationManager(this);
this.manager.setState(this.element, this);
}
componentDidLoad() {
this.manager.savedState();
}
componentShouldUpdate() {
this.manager.setState(this.element, this);
}
componentDidUpdate() {
this.manager.savedState();
}
disconnectedCallback() {
this.destroy();
}
render() {
return h("slot", null);
}
get el() { return getElement(this); }
static get watchers() { return {
"animation": ["animationDidChangeHandler"],
"keyFramesData": ["keyFramesDidChangeHandler"],
"options": ["optionsDidChangeHandler"],
"optionsData": ["optionsDataDidChangeHandler"],
"currentTime": ["setCurrenTime"],
"startTime": ["setStartTime"],
"playbackRate": ["setPlaybackRate"]
}; }
};
const animatableCubeCss = ":host{--size:var(--animatable-cube-size, var(--animatable-cube-width, var(--animatable-cube-height, 100px)));--perspective:var(--animatable-cube-perspective, 1000px);--perspective-origin:var(--animatable-cube-perspective-origin, 50% 50%);--display:var(--animatable-cube-display, flex);--transition-property:var(--animatable-cube-transition-property, (width, height));--transition-duration:var(--animatable-cube-transition-duration, 0);--transition-delay:var(--animatable-cube-transition-delay, 0);--transition-timing-function:var(--animatable-cube-transition-timing-function, initial);--align-items:var(--animatable-cube-align-items, center);--justify-content:var(--animatable-cube-justify-content, center);--margin:var(--animatable-cube-margin, 0);--padding:var(--animatable-cube-padding, 0);--face-border:var(--animatable-cube-face-border, none);--face-background:var(--animatable-cube-face-background, white);--face-background-size:var(--animatable-cube-face-background-size, 100% 100%);--face-animation:var(\n --animatable-cube-face-animation,\n backgroundAnimation\n var(--animatable-cube-face-animation-duration, 0s)\n var(--animatable-cube-face-animation-timing-function, initial)\n var(--animatable-cube-face-animation-delay, 0s)\n var(--animatable-cube-face-animation-iteration-count, infinite)\n var(--animatable-cube-face-animation-direction, alternate)\n );--face-size:calc(var(--size)/2);--face-transform-origin:var(--animatable-cube-face-transform-origin, center center);perspective:var(--perspective);perspective-origin:var(--perspective-origin);padding:var(--padding);margin:var(--margin);display:var(--display);align-items:var(--align-items);justify-content:var(--justify-content);width:var(--size);height:var(--size);transition-property:var(--transition-property);transition-duration:var(--transition-duration);transition-delay:var(--transition-delay);transition-timing-function:var(--transition-timing-function)}:host>div{display:block;height:100%;width:100%;max-width:100%;position:relative;background:transparent;transform-style:preserve-3d;transform:var(--animatable-cube-transform, translateZ(-var(--face-size)));transform-origin:var(--animatable-cube-transform-origin, initial);transition:transform var(--transition-duration)}.face{background:var(--animatable-cube-face-background, var(--face-background));position:absolute;height:100%;width:100%;border:var(--face-border);transition:transform var(--transition-duration);transform-origin:var(--face-transform-origin)}.front{transform:rotateY(0deg)\n translateZ(var(--face-size));will-change:transform;background:var(--animatable-cube-front-face-background, var(--face-background));background-size:var(--animatable-cube-front-face-background-size, var(--face-background-size));animation:var(--animatable-cube-front-face-animation, var(--face-animation))}.back{transform:rotateY(180deg)\n translateZ(var(--face-size));will-change:transform;background:var(--animatable-cube-back-face-background, var(--face-background));background-size:var(--animatable-cube-back-face-background-size, var(--face-background-size));animation:var(--animatable-cube-back-face-animation, var(--face-animation))}.right{transform:rotateY(90deg)\n translateZ(var(--face-size));will-change:transform;background:var(--animatable-cube-right-face-background, var(--face-background));background-size:var(--animatable-cube-right-face-background-size, var(--face-background-size));animation:var(--animatable-cube-right-face-animation, var(--face-animation))}.left{transform:rotateY(-90deg)\n translateZ(var(--face-size));will-change:transform;background:var(--animatable-cube-left-face-background, var(--face-background));background-size:var(--animatable-cube-left-face-background-size, var(--face-background-size));animation:var(--animatable-cube-left-face-animation, var(--face-animation))}.top{transform:rotateX(90deg)\n translateZ(var(--face-size));will-change:transform;background:var(--animatable-cube-top-face-background, var(--face-background));background-size:var(--animatable-cube-top-face-background-size, var(--face-background-size));animation:var(--animatable-cube-top-face-animation, var(--face-animation))}.bottom{transform:rotateX(-90deg)\n translateZ(var(--face-size));will-change:transform;background:var(--animatable-cube-bottom-face-background, var(--face-background));background-size:var(--animatable-cube-bottom-face-background-size, var(--face-background-size));animation:var(--animatable-cube-bottom-face-animation, var(--face-animation))}@keyframes backgroundAnimation{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}";
const Cube = class {
constructor(hostRef) {
registerInstance(this, hostRef);
this.onStart = createEvent(this, "start", 3);
this.onFinish = createEvent(this, "finish", 3);
this.onCancel = createEvent(this, "cancel", 3);
/**
* Animation manager for Animatable
*/
this.manager = null;
/**
* Start the animation when the component is mounted.
*/
this.autoPlay = false;
}
get element() {
return this.el.shadowRoot.querySelector(':host > div');
}
animationDidChangeHandler(animation) {
this.keyFrames = getKeyFramesByAnimation(animation);
}
/**
* Get keyFrames of the animation from string data.
* @param text - The string with the keyFrames of the animation.
*/
keyFramesDidChangeHandler(text) {
if (text !== undefined)
this.keyFrames = JSON.parse(text);
}
/**
* Get options of the animation from string data.
* @param text - The string with the options of the animation.
*/
optionsDidChangeHandler(options) {
clearPropsWithOptions(this, options);
}
/**
* Get options of the animation from string data.
* @param text - The string with the options of the animation.
*/
optionsDataDidChangeHandler(text) {
if (text !== undefined)
this.options = JSON.parse(text);
}
setCurrenTime(newValue) {
this.manager.currentAnimation.currentTime = newValue;
}
/**
* Returns the current time value of the animation in milliseconds, whether running or paused.
*/
async getCurrentTime() {
return Promise.resolve(this.manager.currentAnimation.currentTime);
}
setStartTime(newValue) {
this.manager.currentAnimation.startTime = newValue;
}
/**
* Returns the scheduled time when an animation's playback should begin.
*/
async getStartTime() {
return Promise.resolve(this.manager.currentAnimation.startTime);
}
/**
* Indicates whether the animation is currently waiting
* for an asynchronous operation such as initiating playback
* or pausing a running animation.
*/
async getPending() {
return Promise.resolve(this.manager.currentAnimation.pending);
}
setPlaybackRate(newValue) {
this.manager.currentAnimation.playbackRate = newValue;
}
/**
* Returns the playback rate of the animation.
*/
async getPlaybackRate() {
return Promise.resolve(this.manager.currentAnimation.playbackRate);
}
/**
* Returns an enumerated value describing the playback state of an animation.
*/
async getPlayState() {
return Promise.resolve(this.manager.currentAnimation.playState);
}
/**
* Clears all `KeyframeEffects` caused by this animation and aborts its playback.
*/
async cancel() {
this.manager.currentAnimation.cancel();
}
/**
* Sets the current playback time to the end of the animation
* corresponding to the current playback direction.
*/
async finish() {
this.manager.currentAnimation.finish();
}
/**
* Suspends playback of the animation.
*/
async pause() {
this.manager.currentAnimation.pause();
}
/**
* Starts or resumes playing of an animation.
*/
async play() {
this.manager.playAnimation();
}
/**
* Reverses the playback direction, meaning the animation ends at its beginning.
*/
async reverse() {
this.manager.currentAnimation.reverse();
}
/**
* Clear the current animation
*/
async clear() {
this.manager.clearAnimation();
}
/**
* Destroy the current animation
*/
async destroy() {
if (this.manager !== null) {
this.manager.destroyAnimation();
}
}
/**
* Initialize manager
*/
connectedCallback() {
this.manager = new AnimationManager(this);
}
componentDidLoad() {
this.manager.setState(this.element, this);
this.manager.savedState();
}
componentShouldUpdate() {
this.manager.setState(this.element, this);
}
componentDidUpdate() {
this.manager.savedState();
}
disconnectedCallback() {
this.destroy();
}
render() {
return (h(Host, null, h("div", null, h("div", { class: "front face" }, h("slot", { name: 'front-face' })), h("div", { class: "back face" }, h("slot", { name: 'back-face' })), h("div", { class: "right face" }, h("slot", { name: 'right-face' })), h("div", { class: "left face" }, h("slot", { name: 'left-face' })), h("div", { class: "top face" }, h("slot", { name: 'top-face' })), h("div", { class: "bottom face" }, h("slot", { name: 'bottom-face' })), h("slot", null))));
}
get el() { return getElement(this); }
static get watchers() { return {
"animation": ["animationDidChangeHandler"],
"keyFramesData": ["keyFramesDidChangeHandler"],
"options": ["optionsDidChangeHandler"],
"optionsData": ["optionsDataDidChangeHandler"],
"currentTime": ["setCurrenTime"],
"startTime": ["setStartTime"],
"playbackRate": ["setPlaybackRate"]
}; }
};
Cube.style = animatableCubeCss;
export { AnimatableComponent as animatable_component, Cube as animatable_cube };