Skip to content

Commit

Permalink
fix(staggered-fade-and-slide): filter already animated elements
Browse files Browse the repository at this point in the history
  • Loading branch information
victorcg88 committed Aug 19, 2024
1 parent fb7a2df commit b6e60c7
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,25 +75,17 @@ describe('testing StaggeredFadeAndSlide component', () => {
expect(elements[1].element.style.transitionDelay).not.toBe('0ms');
expect(elements[2].element.style.transitionDelay).not.toBe('0ms');

expect(elements[0].element.dataset.animated).toBe('true');
expect(elements[1].element.dataset.animated).toBeUndefined();
expect(elements[2].element.dataset.animated).toBeUndefined();

// Once the previous transition is finished wait for the stagger delay
jest.advanceTimersByTime(stagger);

expect(elements[1].element.style.transitionDelay).toBe('0ms');
expect(elements[2].element.style.transitionDelay).not.toBe('0ms');

expect(elements[1].element.dataset.animated).toBe('true');
expect(elements[2].element.dataset.animated).toBeUndefined();

// Once the previous transition is finished wait for the stagger delay
jest.advanceTimersByTime(stagger);

for (const el of elements) {
expect(el.element.style.transitionDelay).toBe('0ms');
expect(el.element.dataset.animated).toBe('true');
}
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, onUpdated } from 'vue';
import { useDisableAnimation } from './use-disable-animation';
/**
Expand Down Expand Up @@ -42,10 +42,22 @@
setup(props) {
/** The duration of the transition in ms. */
const transitionDuration = 250;
/** Indicates if there are new elements to animate. */
let isNewSet = true;
/** The new elements to animate. */
let elementsToAnimate: Element[] = [];
/** The name of the animation. */
const { name } = useDisableAnimation('x-staggered-fade-and-slide');
/**
* When the component is updated, we are considering that
* a new set of elements is being inserted, so we need
* to refresh the elements to animate.
*/
onUpdated(() => {
isNewSet = true;
});
/**
* Listener called when one frame the element is inserted.
* This calculates the stagger delay to be used as `transitionDelay` and finally resolve
Expand All @@ -54,26 +66,32 @@
* @param el - Element inserted.
* @param done - Callback to indicate the transition end.
*/
function onEnter(el: HTMLElement, done: () => void) {
const elIndex = findAnimationIndex(el);
function onEnter(el: Element, done: () => void) {
if (isNewSet) {
refreshElementsToAnimate(el);
}
const elIndex = elementsToAnimate.indexOf(el);
const staggerDelay = elIndex > 0 ? elIndex * props.stagger : 0;
el.style.transitionDelay = `${staggerDelay}ms`;
(el as HTMLElement).style.transitionDelay = `${staggerDelay}ms`;
setTimeout(done, transitionDuration + staggerDelay);
}
/**
* Finds the index of the element in the parent children subset of new elements entering the DOM.
* This is achived by filtering out the elements that are already animated,
* which are those marked with the `data-animated` attribute.
* Finds he parent children subset of new elements entering the DOM.
* This is achieved by filtering out the elements that are already animated.
* Those with 'transition-delay' equal to '0ms' are considered already animated.
*
* Also marks isNewSet as false as the elements are already updated.
*
* @param el - Element to find.
* @returns The index of the element in the parent children subset of new elements.
* @param el - Current element.
*/
function findAnimationIndex(el: HTMLElement) {
return [...el.parentElement!.children]
.filter(c => (c as HTMLElement).dataset.animated !== 'true')
.indexOf(el);
function refreshElementsToAnimate(el: Element) {
elementsToAnimate = [...el.parentElement!.children].filter(
c => (c as HTMLElement).style.transitionDelay !== '0ms'
);
isNewSet = false;
}
/**
Expand All @@ -83,9 +101,8 @@
*
* @param el - Element inserted.
*/
function onAfterEnter(el: HTMLElement) {
el.style.transitionDelay = '0ms';
el.dataset.animated = 'true';
function onAfterEnter(el: Element) {
(el as HTMLElement).style.transitionDelay = '0ms';
}
return {
Expand Down
6 changes: 5 additions & 1 deletion packages/x-components/src/views/home/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@
<template #sliding-panel-left-button>
<ChevronLeft />
</template>
<RelatedTags class="x-gap-8" itemClass="x-tag-outlined" />
<RelatedTags
:animation="resultsAnimation"
class="x-gap-8"
itemClass="x-tag-outlined"
/>
<template #sliding-panel-right-button>
<ChevronRight />
</template>
Expand Down

0 comments on commit b6e60c7

Please sign in to comment.