fix: collapse behavior when releasing gesture

This commit is contained in:
Sean Perkins
2024-01-16 22:04:52 -05:00
parent b10a3824c8
commit 7e2ccaaaf1
2 changed files with 70 additions and 42 deletions

View File

@@ -6,6 +6,7 @@ import { createAnimation } from '@utils/animation/animation';
* @param viewportOffset The offset of the element on the x-axis to locate it off-screen.
* @param progress The progress of the animation (0 - 1).
* @param isFinal Whether the animation is in its final state (collapsed or expanded).
* @param isReset Whether to reset the element to its initial state.
* @param zIndex _Optional_ The z-index to apply to the element.
*/
export const slidingItemAnimation = (
@@ -13,11 +14,12 @@ export const slidingItemAnimation = (
viewportOffset: number,
progress: number,
isFinal: boolean,
isReset: boolean,
zIndex: number | undefined
) => {
const styles = zIndex !== undefined ? { zIndex: `${zIndex}` } : {};
const animation = createAnimation()
const swipeAnimation = createAnimation()
.addElement(el)
/**
* The specific value here does not actually
@@ -32,12 +34,28 @@ export const slidingItemAnimation = (
{ offset: 1, transform: `translate3d(0,0,0)`, ...styles },
]);
if (!isFinal) {
animation.progressStart(true, progress);
} else {
animation.destroy();
if (isReset) {
swipeAnimation.destroy();
const toggleAnimation = createAnimation()
const leaveAnimation = createAnimation()
.addElement(el)
.duration(300)
.easing('ease-out')
.beforeStyles({
zIndex: zIndex,
})
.fromTo('transform', el.style.transform, `translate3d(${viewportOffset}px,0,0)`)
.afterClearStyles(['z-index']);
leaveAnimation.play().then(() => leaveAnimation.destroy());
}
if (!isFinal) {
swipeAnimation.progressStart(true, progress);
} else {
swipeAnimation.destroy();
const expandAnimation = createAnimation()
.addElement(el)
.duration(300)
.easing('ease-out')
@@ -48,6 +66,6 @@ export const slidingItemAnimation = (
.fromTo('transform', el.style.transform, `translate3d(0,0,0)`)
.afterClearStyles(['z-index']);
toggleAnimation.play();
expandAnimation.play();
}
};

View File

@@ -454,7 +454,7 @@ export class ItemSliding implements ComponentInterface {
return;
}
const { el, optsWidthRightSide, optsWidthLeftSide } = this;
const { el, optsWidthRightSide, optsWidthLeftSide, animationType } = this;
const rtl = isRTL(el);
@@ -501,44 +501,16 @@ export class ItemSliding implements ComponentInterface {
openSlidingItem = undefined;
style.transform = '';
if (animationType === 'modern') {
this.animateSlidingItemOptions(0, rtl);
}
return;
}
style.transform = `translate3d(${-openAmount}px,0,0)`;
if (this.animationType === 'modern') {
/**
* The total width of the 'processed' options.
* Used to calculate the offset to move each option off the
* screen, on top of each other.
*/
let optionWidthOffset = 0;
const side = this.state === SlidingState.Start ? 'start' : 'end';
const options = Array.from(this.getOptions(side)?.querySelectorAll('ion-item-option') || []);
const optsWidth = this.getOptionsWidth(side);
const progress = Math.abs(openAmount / optsWidth);
options.forEach((option, index) => {
let isFinal = false;
let zIndex: number | undefined;
let viewportOffset = 0;
if (side === 'start') {
viewportOffset = rtl ? -1 * (optsWidth - optionWidthOffset) : -1 * (option.clientWidth + optionWidthOffset);
isFinal = openAmount <= -optsWidth;
zIndex = rtl ? undefined : options.length - index;
} else {
viewportOffset = rtl ? optionWidthOffset + option.clientWidth : optsWidth - optionWidthOffset;
isFinal = openAmount >= optsWidth;
zIndex = rtl ? options.length - index : undefined;
}
slidingItemAnimation(option, viewportOffset, progress, isFinal, zIndex);
optionWidthOffset += option.clientWidth;
});
if (animationType === 'modern') {
this.animateSlidingItemOptions(openAmount, rtl);
}
this.ionDrag.emit({
@@ -547,6 +519,44 @@ export class ItemSliding implements ComponentInterface {
});
}
private animateSlidingItemOptions(openAmount: number, isRTL: boolean) {
/**
* The total width of the 'processed' options.
* Used to calculate the offset to move each option off the
* screen, on top of each other.
*/
let optionWidthOffset = 0;
const side = this.state === SlidingState.Start ? 'start' : 'end';
const options = Array.from(this.getOptions(side)?.querySelectorAll('ion-item-option') || []);
const optsWidth = this.getOptionsWidth(side);
const progress = Math.abs(openAmount / optsWidth);
options.forEach((option, index) => {
let isFinal = false;
let zIndex: number | undefined;
let viewportOffset = 0;
const isReset = openAmount === 0;
if (side === 'start') {
viewportOffset = isRTL ? -1 * (optsWidth - optionWidthOffset) : -1 * (option.clientWidth + optionWidthOffset);
isFinal = openAmount <= -optsWidth;
zIndex = isRTL ? undefined : options.length - index;
} else {
viewportOffset = isRTL ? optionWidthOffset + option.clientWidth : optsWidth - optionWidthOffset;
isFinal = openAmount >= optsWidth;
zIndex = isRTL ? options.length - index : undefined;
}
slidingItemAnimation(option, viewportOffset, progress, isFinal, isReset, zIndex);
optionWidthOffset += option.clientWidth;
});
}
private getSlidingRatioSync(): number {
if (this.openAmount > 0) {
return this.openAmount / this.optsWidthRightSide;