StackedRouter: new router simulates app behaviour #45
1 changed files with 95 additions and 1 deletions
|
@ -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} />
|
||||||
|
|
Loading…
Reference in a new issue