StackedRouter: prototype of generic swipe to back

This commit is contained in:
thislight 2024-11-18 18:08:14 +08:00
parent 6fbd198021
commit a3ef5d9cf5
No known key found for this signature in database
GPG key ID: FCFE5192241CCD4E

View file

@ -15,7 +15,10 @@ import { createStore, unwrap } from "solid-js/store";
import "./StackedRouter.css"; import "./StackedRouter.css";
import { animateSlideInFromRight, animateSlideOutToRight } from "./anim"; import { animateSlideInFromRight, animateSlideOutToRight } from "./anim";
import { ANIM_CURVE_DECELERATION, ANIM_CURVE_STD } from "../material/theme"; import { ANIM_CURVE_DECELERATION, ANIM_CURVE_STD } from "../material/theme";
import { makeEventListener } from "@solid-primitives/event-listener"; import {
eventListener,
makeEventListener,
} from "@solid-primitives/event-listener";
export type StackedRouterProps = Omit<RouterProps, "url">; export type StackedRouterProps = Omit<RouterProps, "url">;
@ -289,6 +292,93 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
}); });
}; };
let reenterableAnimation: Animation | undefined;
let origX = 0,
origWidth = 0;
const resetAnimation = () => {
reenterableAnimation = undefined;
};
const onDialogTouchStart = (
event: TouchEvent & { currentTarget: HTMLDialogElement },
) => {
if (event.touches.length !== 1) {
return;
}
const [fig0] = event.touches;
const { x, width } = event.currentTarget.getBoundingClientRect();
if (fig0.clientX < x - 22 || fig0.clientX > x + 22) {
return;
}
origX = x;
origWidth = width;
const lastFr = stack[stack.length - 1];
const createAnimation = lastFr.animateClose ?? animateClose;
reenterableAnimation = createAnimation(event.currentTarget);
reenterableAnimation.pause();
reenterableAnimation.addEventListener("finish", resetAnimation);
reenterableAnimation.addEventListener("cancel", resetAnimation);
};
const onDialogTouchMove = (
event: TouchEvent & { currentTarget: HTMLDialogElement },
) => {
if (event.touches.length !== 1) {
if (reenterableAnimation) {
reenterableAnimation.reverse();
reenterableAnimation.play();
}
}
if (!reenterableAnimation) return;
event.preventDefault();
event.stopPropagation();
const [fig0] = event.touches;
const ofsX = fig0.clientX - origX;
const pc = ofsX / origWidth / window.devicePixelRatio;
const { activeDuration, delay } =
reenterableAnimation.effect!.getComputedTiming();
const totalTime = (delay || 0) + Number(activeDuration);
reenterableAnimation.currentTime = totalTime * pc;
};
const onDialogTouchEnd = (event: TouchEvent) => {
if (!reenterableAnimation) return;
event.preventDefault();
event.stopPropagation();
const { activeDuration, delay } =
reenterableAnimation.effect!.getComputedTiming();
const totalTime = (delay || 0) + Number(activeDuration);
if (
Number(reenterableAnimation.currentTime) / totalTime >
0.1
) {
reenterableAnimation.addEventListener("finish", () => {
onlyPopFrame(1);
});
reenterableAnimation.play();
} else {
reenterableAnimation.cancel();
}
};
const onDialogTouchCancel = (event: TouchEvent) => {
if (!reenterableAnimation) return;
event.preventDefault();
event.stopPropagation();
reenterableAnimation.cancel();
};
return ( return (
<NavigatorContext.Provider <NavigatorContext.Provider
value={{ value={{
@ -325,6 +415,10 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
class="StackedPage" class="StackedPage"
onCancel={[popFrame, 1]} onCancel={[popFrame, 1]}
onClick={[onDialogClick, popFrame]} onClick={[onDialogClick, popFrame]}
onTouchStart={onDialogTouchStart}
onTouchMove={onDialogTouchMove}
onTouchEnd={onDialogTouchEnd}
onTouchCancel={onDialogTouchCancel}
id={frame().rootId} id={frame().rootId}
> >
<StaticRouter url={frame().path} {...oprops} /> <StaticRouter url={frame().path} {...oprops} />