diff --git a/README.md b/README.md index f739b73..023be0f 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ And then of course you should configure classnames and other parameters in JavaS * @default * undefined */ - prefix?: string; + prefix: string; /** * ClassName which element contains before animation starts and should be deleted when animation starts * @default '' @@ -134,7 +134,7 @@ And then of course you should configure classnames and other parameters in JavaS /** * ClassName which element contains during the whole animation. * Usually there you write `animation` or `transition` css properties. - * (on: 1st or 2nd frame, off: last frame) + * (on: 1st frame, off: last frame) * @default '' */ active: string; @@ -156,14 +156,15 @@ And then of course you should configure classnames and other parameters in JavaS animation: { /** * When you add animations with css, you can do this either with `transition` or with `animation` property - * You should choose one of this for correct animation end detecting. - * *If you have problems with detecting animation end, see `animation.end`* - * @default 'transition' + * You should choose one of this for correct animation end detecting if you have problems. + * By default ElAnimate adds both `transitionend` and `animationend` handlers + * As a last resort, you can set `animation.name` + * @default 'both' */ - type: 'transition' | 'animation'; + type: 'transition' | 'animation' | 'both'; /** * if you have several animation, you can additionally set `animation.name` field in config - * Then el-animate can properly detect animation end with this animation name. + * Then ElAnimate can properly detect animation end with this animation name. * You should type the same string as in @keyframes */ name: string | undefined; @@ -192,7 +193,7 @@ And then of course you should configure classnames and other parameters in JavaS * - `restart` means that we should stop current animation and start the new one * - `replaceToState` means that we only replace final points of animation * (el-animate replaces `to` className and animation end handler). **Useful if `animation.type` is `transition`. - * Then animtion Then the animation continues without interruption** + * Then the animation continues without interruption** * @default 'restart' */ multiCallHandling: 'block' | 'restart' | 'replaceToState'; @@ -203,7 +204,7 @@ Also, if in some moment you need to cancel animation and return it to initial po ## Demos -All demos are available [here on codepen.io](https://codepen.io/collection/warqKo?grid_type=list) +All demos are available [here on codepen.io](https://codepen.io/collection/warqKo?grid_type=list). We highly recommend to view them. ## Build diff --git a/playground/scripts/index.js b/playground/scripts/index.js index 00adcc6..7206f08 100644 --- a/playground/scripts/index.js +++ b/playground/scripts/index.js @@ -47,6 +47,16 @@ changeStateButton.addEventListener('click', () => { }, multiCallHandling: 'replaceToState', }); + animate(target, { + classNames: { + prefix: 'enter', + initial: 'hidden', + }, + animation: { + type: 'animation', + }, + multiCallHandling: 'replaceToState', + }); return; } @@ -58,4 +68,11 @@ changeStateButton.addEventListener('click', () => { }, multiCallHandling: 'replaceToState', }); + animate(target, { + classNames: { + prefix: 'leave', + final: 'hidden', + }, + multiCallHandling: 'replaceToState', + }); }); diff --git a/src/animate/animation-end-handler.ts b/src/animate/animation-end-handler.ts index c42c92b..86889d3 100644 --- a/src/animate/animation-end-handler.ts +++ b/src/animate/animation-end-handler.ts @@ -27,36 +27,67 @@ export function removeAnimationEndEventListener(elem: HTMLElement) { return; } - elem.removeEventListener(animationInfo.eventName, animationInfo.handler); + animationInfo.handlers.forEach((handler) => { + elem.removeEventListener(handler.eventName, handler.function); + }); setIdleState(elem, animationInfo.classNames); clearAnimationInformation(elem); } function addAnimationEndEventListener(elem: HTMLElement, config: Config, cb: () => void) { const { animation, classNames } = config; - const eventName = getEventName(animation.type); - + // Create handlers const baseHandler = () => { removeAnimationEndEventListener(elem); cb(); }; - const cssAnimationHandler = (evt: AnimationEvent) => { + const cssAnimationHandler = (evt: AnimationEvent | TransitionEvent) => { + const animEvt = evt as AnimationEvent; // If user specified animation name, we should call cb only if event refers to the right animation - if (evt.animationName && animation.name && evt.animationName !== animation.name) { + if (animEvt.animationName && animation.name && animEvt.animationName !== animation.name) { return; } baseHandler(); }; + // Choose which handlers should add to elem + if (animation.type === 'both') { + elem.addEventListener(AnimationEndEvent.TRANSITION, baseHandler); + elem.addEventListener(AnimationEndEvent.ANIMATION, cssAnimationHandler); + setAnimationInformation(elem, { + classNames, + handlers: [ + { + eventName: AnimationEndEvent.TRANSITION, + function: baseHandler, + }, + { + eventName: AnimationEndEvent.ANIMATION, + function: cssAnimationHandler, + }, + ], + }); + return; + } + const handler = (evt: TransitionEvent | AnimationEvent) => animation.type === AnimationType.ANIMATION ? cssAnimationHandler(evt as AnimationEvent) : baseHandler(); + const eventName = getEventName(animation.type); elem.addEventListener(eventName, handler); - setAnimationInformation(elem, { handler, classNames, eventName }); + setAnimationInformation(elem, { + classNames, + handlers: [ + { + eventName, + function: handler, + }, + ], + }); } export default function createAnimationEndHandler( diff --git a/src/animate/animation-information.ts b/src/animate/animation-information.ts index 6f18fe4..04fd59c 100644 --- a/src/animate/animation-information.ts +++ b/src/animate/animation-information.ts @@ -9,9 +9,13 @@ const ANIMATION_END_PROPERTY_SETTINGS: PropertyDescriptor = { type AnimationEndHandler = (evt: AnimationEvent | TransitionEvent) => void; -type AnimationInformation = { - handler: AnimationEndHandler; +type EventListenerHandler = { + function: AnimationEndHandler; eventName: AnimationEndEvent; +}; + +type AnimationInformation = { + handlers: EventListenerHandler[]; classNames: Config['classNames']; }; diff --git a/src/animate/config.ts b/src/animate/config.ts index cac629e..e80530f 100644 --- a/src/animate/config.ts +++ b/src/animate/config.ts @@ -1,4 +1,4 @@ -import { AnimationType, AnimationTypeUnion } from '../helpers/enum'; +import { AnimationTypeUnion } from '../helpers/enum'; type Callback = (elem: HTMLElement, currentConfig: Config) => void; @@ -29,7 +29,7 @@ export interface Config { /** * ClassName which element contains during the whole animation. * Usually there you write `animation` or `transition` css properties. - * (on: 1st or 2nd frame, off: last frame) + * (on: 1st frame, off: last frame) * @default '' */ active: string; @@ -51,14 +51,15 @@ export interface Config { animation: { /** * When you add animations with css, you can do this either with `transition` or with `animation` property - * You should choose one of this for correct animation end detecting. - * *If you have problems with detecting animation end, see `animation.end`* - * @default 'transition' + * By default ElAnimate adds both `transitionend` and `animationend` handlers + * You should choose one of this for correct animation end detecting if you have problems. + * As a last resort, you can set `animation.name` + * @default 'both' */ - type: AnimationTypeUnion; + type: AnimationTypeUnion | 'both'; /** * if you have several animation, you can additionally set `animation.name` field in config - * Then el-animate can properly detect animation end with this animation name. + * Then ElAnimate can properly detect animation end with this animation name. * You should type the same string as in @keyframes */ name: string | undefined; @@ -87,7 +88,7 @@ export interface Config { * - `restart` means that we should stop current animation and start the new one * - `replaceToState` means that we only replace final points of animation * (el-animate replaces `to` className and animation end handler). **Useful if `animation.type` is `transition`. - * Then animtion Then the animation continues without interruption (See examples on github)** + * Then the animation continues without interruption (See examples on github)** * @default 'restart' */ multiCallHandling: 'block' | 'restart' | 'replaceToState'; @@ -108,7 +109,7 @@ export function getConfig(options: Partial): Config { final: options.classNames?.final || '', }, animation: { - type: options.animation?.type || AnimationType.TRANSITION, + type: options.animation?.type || 'both', name: options.animation?.name, }, callbacks: {