StackedRouter: prototype of generic swipe to back
This commit is contained in:
		
							parent
							
								
									6fbd198021
								
							
						
					
					
						commit
						a3ef5d9cf5
					
				
					 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue