Transition: Fixes for controlled swipe
This commit is contained in:
parent
e77506cc54
commit
939fff460e
@ -97,11 +97,12 @@ function Transition({
|
||||
const rendersRef = useRef<Record<number, React.ReactNode | ChildrenFn>>({});
|
||||
const prevActiveKey = usePrevious<any>(activeKey);
|
||||
const forceUpdate = useForceUpdate();
|
||||
const isAnimatingRef = useRef(false);
|
||||
const isSwipeJustCancelledRef = useRef(false);
|
||||
|
||||
const activeKeyChanged = prevActiveKey !== undefined && activeKey !== prevActiveKey;
|
||||
const hasActiveKeyChanged = prevActiveKey !== undefined && activeKey !== prevActiveKey;
|
||||
|
||||
if (!renderCount && activeKeyChanged) {
|
||||
if (!renderCount && hasActiveKeyChanged) {
|
||||
rendersRef.current = { [prevActiveKey]: rendersRef.current[prevActiveKey] };
|
||||
}
|
||||
|
||||
@ -149,25 +150,26 @@ function Transition({
|
||||
}
|
||||
});
|
||||
|
||||
if (!activeKeyChanged) {
|
||||
if (childElements.length === 1 || (nextKey !== undefined && childElements.length === 2)) {
|
||||
const firstChild = childNodes[activeIndex] as HTMLElement;
|
||||
|
||||
addExtraClass(firstChild, CLASSES.active);
|
||||
|
||||
if (isSlideOptimized) {
|
||||
setExtraStyles(firstChild, {
|
||||
transition: 'none',
|
||||
transform: 'translate3d(0, 0, 0)',
|
||||
});
|
||||
}
|
||||
|
||||
if (childElements.length === 2) {
|
||||
const nextChild = childElements[0] === firstChild ? childElements[1] : childElements[0];
|
||||
addExtraClass(nextChild, CLASSES.inactive);
|
||||
}
|
||||
if (!hasActiveKeyChanged) {
|
||||
if (isAnimatingRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
childElements.forEach((childElement) => {
|
||||
if (childElement === childNodes[activeIndex]) {
|
||||
addExtraClass(childElement, CLASSES.active);
|
||||
|
||||
if (isSlideOptimized) {
|
||||
setExtraStyles(childElement, {
|
||||
transition: 'none',
|
||||
transform: 'translate3d(0, 0, 0)',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
addExtraClass(childElement, CLASSES.inactive);
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -185,6 +187,7 @@ function Transition({
|
||||
cleanup,
|
||||
activeKey,
|
||||
currentKeyRef,
|
||||
isAnimatingRef,
|
||||
container,
|
||||
childNodes[activeIndex],
|
||||
childNodes[prevActiveIndex],
|
||||
@ -224,6 +227,7 @@ function Transition({
|
||||
}
|
||||
});
|
||||
|
||||
isAnimatingRef.current = true;
|
||||
const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent();
|
||||
onStart?.();
|
||||
|
||||
@ -260,6 +264,7 @@ function Transition({
|
||||
|
||||
onStop?.();
|
||||
dispatchHeavyAnimationStop();
|
||||
isAnimatingRef.current = false;
|
||||
|
||||
cleanup();
|
||||
});
|
||||
@ -281,6 +286,7 @@ function Transition({
|
||||
isSwipeJustCancelledRef.current = true;
|
||||
onStop?.();
|
||||
dispatchHeavyAnimationStop();
|
||||
isAnimatingRef.current = false;
|
||||
},
|
||||
);
|
||||
} else {
|
||||
@ -293,7 +299,7 @@ function Transition({
|
||||
activeKey,
|
||||
nextKey,
|
||||
prevActiveKey,
|
||||
activeKeyChanged,
|
||||
hasActiveKeyChanged,
|
||||
isBackwards,
|
||||
name,
|
||||
onStart,
|
||||
@ -373,6 +379,7 @@ function performSlideOptimized(
|
||||
cleanup: NoneToVoidFunction,
|
||||
activeKey: number,
|
||||
currentKeyRef: { current: number | undefined },
|
||||
isAnimatingRef: { current: boolean | undefined },
|
||||
container: HTMLElement,
|
||||
toSlide: ChildNode,
|
||||
fromSlide?: ChildNode,
|
||||
@ -409,8 +416,8 @@ function performSlideOptimized(
|
||||
isBackwards = !isBackwards;
|
||||
}
|
||||
|
||||
isAnimatingRef.current = true;
|
||||
const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent();
|
||||
|
||||
onStart?.();
|
||||
|
||||
toggleExtraClass(container, `Transition-${name}`, !isBackwards);
|
||||
@ -476,6 +483,8 @@ function performSlideOptimized(
|
||||
|
||||
onStop?.();
|
||||
dispatchHeavyAnimationStop();
|
||||
isAnimatingRef.current = false;
|
||||
|
||||
cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
@ -56,7 +56,7 @@ type AnimateNumberProps<T extends number | number[]> = {
|
||||
duration: number;
|
||||
onUpdate: (value: T) => void;
|
||||
timing?: TimingFn;
|
||||
onEnd?: () => void;
|
||||
onEnd?: (isCanceled?: boolean) => void;
|
||||
};
|
||||
|
||||
export const timingFunctions = {
|
||||
@ -87,13 +87,15 @@ export function animateNumber<T extends number | number[]>({
|
||||
to,
|
||||
}: AnimateNumberProps<T>) {
|
||||
const t0 = Date.now();
|
||||
let canceled = false;
|
||||
|
||||
let isCanceled = false;
|
||||
|
||||
animateInstantly(() => {
|
||||
if (canceled) return false;
|
||||
if (isCanceled) return false;
|
||||
|
||||
const t1 = Date.now();
|
||||
let t = (t1 - t0) / duration;
|
||||
if (t > 1) t = 1;
|
||||
const t = Math.min((t1 - t0) / duration, 1);
|
||||
|
||||
const progress = timing(t);
|
||||
if (typeof from === 'number' && typeof to === 'number') {
|
||||
onUpdate((from + ((to - from) * progress)) as T);
|
||||
@ -101,13 +103,17 @@ export function animateNumber<T extends number | number[]>({
|
||||
const result = from.map((f, i) => f + ((to[i] - f) * progress));
|
||||
onUpdate(result as T);
|
||||
}
|
||||
if (t === 1 && onEnd) onEnd();
|
||||
|
||||
if (t === 1) {
|
||||
onEnd?.();
|
||||
}
|
||||
|
||||
return t < 1;
|
||||
}, requestMeasure);
|
||||
|
||||
return () => {
|
||||
canceled = true;
|
||||
if (onEnd) onEnd();
|
||||
isCanceled = true;
|
||||
onEnd?.(true);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -38,11 +38,10 @@ export function logUnequalProps(currentProps: AnyLiteral, newProps: AnyLiteral,
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(msg);
|
||||
|
||||
for (const prop of currentKeys) {
|
||||
currentKeys.forEach((prop) => {
|
||||
if (currentProps[prop] !== newProps[prop]) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(debugKey, prop, ':', currentProps[prop], '=>', newProps[prop]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ let isSwipeActive = false;
|
||||
let swipeOffsets: MoveOffsets | undefined;
|
||||
let onDrag: ((offsets: MoveOffsets) => void) | undefined;
|
||||
let onRelease: ((onCancel: NoneToVoidFunction) => void) | undefined;
|
||||
let cancelCurrentReleaseAnimation: NoneToVoidFunction | undefined;
|
||||
|
||||
export function captureControlledSwipe(
|
||||
element: HTMLElement, options: {
|
||||
@ -70,6 +71,8 @@ export function allowSwipeControlForTransition(
|
||||
nextSlide: HTMLElement,
|
||||
onCancelForTransition: NoneToVoidFunction,
|
||||
) {
|
||||
cancelCurrentReleaseAnimation?.();
|
||||
|
||||
if (!isSwipeActive) return;
|
||||
|
||||
const targetPosition = extractAnimationEndPosition(currentSlide);
|
||||
@ -108,7 +111,7 @@ export function allowSwipeControlForTransition(
|
||||
};
|
||||
|
||||
onRelease = (onCancelForClient: NoneToVoidFunction) => {
|
||||
const isCanceled = currentDirection === -1;
|
||||
const isRevertSwipe = currentDirection === -1;
|
||||
|
||||
function cleanup() {
|
||||
currentSlide.getAnimations().forEach((a) => a.cancel());
|
||||
@ -120,21 +123,23 @@ export function allowSwipeControlForTransition(
|
||||
});
|
||||
}
|
||||
|
||||
if (!isCanceled) {
|
||||
if (!isRevertSwipe) {
|
||||
// For some reason animations are not cleared when CSS class is removed
|
||||
waitForAnimationEnd(currentSlide, cleanup);
|
||||
}
|
||||
|
||||
animateNumber({
|
||||
cancelCurrentReleaseAnimation = animateNumber({
|
||||
from: progress,
|
||||
to: isCanceled ? 0 : 1,
|
||||
to: isRevertSwipe ? 0 : 1,
|
||||
duration: INERTIA_DURATION,
|
||||
timing: INERTIA_EASING,
|
||||
onUpdate(releaseProgress) {
|
||||
updateAnimationProgress([currentSlide, nextSlide], releaseProgress);
|
||||
},
|
||||
onEnd() {
|
||||
if (isCanceled) {
|
||||
onEnd(isCanceled = false) {
|
||||
cancelCurrentReleaseAnimation = undefined;
|
||||
|
||||
if (isCanceled || isRevertSwipe) {
|
||||
cleanup();
|
||||
onCancelForTransition();
|
||||
onCancelForClient();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user