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 { animateSlideInFromRight, animateSlideOutToRight } from "./anim";
 | 
			
		||||
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">;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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 (
 | 
			
		||||
    <NavigatorContext.Provider
 | 
			
		||||
      value={{
 | 
			
		||||
| 
						 | 
				
			
			@ -325,6 +415,10 @@ const StackedRouter: Component<StackedRouterProps> = (oprops) => {
 | 
			
		|||
                  class="StackedPage"
 | 
			
		||||
                  onCancel={[popFrame, 1]}
 | 
			
		||||
                  onClick={[onDialogClick, popFrame]}
 | 
			
		||||
                  onTouchStart={onDialogTouchStart}
 | 
			
		||||
                  onTouchMove={onDialogTouchMove}
 | 
			
		||||
                  onTouchEnd={onDialogTouchEnd}
 | 
			
		||||
                  onTouchCancel={onDialogTouchCancel}
 | 
			
		||||
                  id={frame().rootId}
 | 
			
		||||
                >
 | 
			
		||||
                  <StaticRouter url={frame().path} {...oprops} />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue