Panresponder + interpolate not following the touch event

12 Views Asked by At

I have developed my own actionsheet that start from bottom.

now I am trying to include panresponder so the use could close it with drag down.

the actionsheet work but i have to drag my hand to the center of the sheet for the sheet to start moving down.

i have included interpolate which I am gussing that it couseing an issue as i do not start from 0.

could someone have a look and see if there is a solution?

here is the coponent

import View from "./ThemeView";
import Text from "./ThemeText";
import Icon from "./Icons";
import TouchableOpacity from "./TouchableOpacityView";
import { ElementsContext } from "./AppContainer";
import { proc, sleep, newId } from "../Methods";
import {
  useContext,
  useState,
  useEffect,
  useRef
} from "react";
import {
  Animated,
  KeyboardAvoidingView,
  Platform,
  Easing,
  PanResponder
} from "react-native";
const g = require("../GlobalContext").default;
export default ({
  title,
  height,
  children,
  visible,
  onHide,
  speed,
  ready,
  toTop,
  ...props
}: {
  speed?: number;
  children?: any;
  title?: any;
  height: string;
  visible: boolean;
  onHide?: () => void;
  toTop?: boolean;
}) => {
  let getHeight = () => {
    if (
      typeof height === "number" &&
      height > 100
    )
      return height;
    return proc(
      parseFloat(
        (height?.toString() ?? "0").replace(
          /%/g,
          ""
        )
      ),
      g.size.window.height
    );
  };
  g.hook("size", "selection");
  let context = useContext(ElementsContext);
  const [started, setStarted] = useState(false);
  const [isV, setIsV] = useState(false);
  const panResponse = useRef();
  const currentValue = useRef();
  const [onReady, setOnReady] = useState(!ready);
  let interpolate = [
    g.size.window.height - getHeight() + 80,
    g.size.window.height + 50
  ];

  const animTop = useRef(
    new Animated.ValueXY({
      y: -getHeight(),
      x: 0
    })
  ).current;
  const animating = useRef(false);
  let id = useRef(newId());

  const tAnimate = (value: number, fn: any) => {
    Animated.timing(animTop.y, {
      toValue: value,
      duration: (100).sureValue(speed),
      easing: Easing.linear,
      useNativeDriver: true
    }).start(() => {
      fn?.();
    });
  };

  let toggle = async (show: boolean) => {
    while (animating.current || !animTop) return;
    //await sleep(50);
    if (show && ready) setOnReady(false);
    animating.current = true;
    // animTop.flattenOffset();
    tAnimate(interpolate[!show ? 1 : 0], () => {
      animating.current = false;
      if (typeof visible !== "function")
        setIsV(visible);
      else setIsV(visible());
      context.update();
      if (show) setOnReady(true);
      // if (show) animTop.flattenOffset();

      //setIsV(visible);
    });
  };

  g.subscribe(
    () => {
      //setAnimTop(new Animated.Value(-vHeight));
      toggle(isV);
    },
    "size",
    "theme.themeMode"
  );
  if (typeof visible === "function")
    g.subscribe(() => {
      if (!isV) setIsV(visible());
      toggle(visible());
    }, "selection");
  if (typeof visible !== "function")
    useEffect(() => {
      if (!isV) setIsV(visible);
      toggle(visible);
    }, [visible]);

  useEffect(() => {
    setStarted(true);
    let listner = animTop.addListener(
      value => (currentValue.current = value)
    );
    return () => {
      context.remove(id.current);
      context.update();
      listner.remove();
    };
  }, []);

  useEffect(() => {
    if (!panResponse.current && animTop) {
      // animTop.extractOffset();
      let startValue = 0;
      let current = 0;

      panResponse.current = PanResponder.create({
        onPanResponderTerminationRequest: () =>
          false,
        onMoveShouldSetResponderCapture: () =>
          true, //Tell iOS that we are allowing the movement
        onMoveShouldSetPanResponderCapture: () =>
          true, // Same here, tell iOS that we allow dragging
        onPanResponderGrant: (
          e,
          gestureState
        ) => {
          startValue = gestureState.dy;
          animTop.setValue({
            x: 0,
            y: interpolate[0]
          });
          return true;
        },
        onPanResponderMove: Animated.event(
          [
            null,
            { dx: animTop.x, dy: animTop.y }
          ],
          { useNativeDriver: false }
        ),
        onPanResponderRelease: (
          evt,
          gestureState
        ) => {
          let old = interpolate[0];
          let newValue = gestureState.dy;
          let diff = newValue - startValue;

          if (Math.abs(diff) > old / 2) {
            onHide(!visible);
          } else {
            //animTop.flattenOffset();
            tAnimate(old); // reset to start value
          }
          return false;
        }
      });
    }
    let op = !context.has(id.current)
      ? context.push.bind(context)
      : context.updateProps.bind(context);
    op(
      <>
        <TouchableOpacity
          ifTrue={isV}
          onPress={() => {
            (onHide || setIsV)(false);
          }}
          css="blur flex"
        />
        <Animated.View
          style={[
            {
              height: getHeight(),
              transform: [
                {
                  translateY:
                    animTop.y.interpolate({
                      inputRange: interpolate,
                      outputRange: interpolate,
                      extrapolate: "clamp"
                    })
                }
              ]
            },
            "absolute mah:95% overflow clearwidth juc:flex-start boTLR:25 botrr:25".css()
          ]}
          {...(panResponse.current?.panHandlers ??
            {})}>
          <View
            invertColor={true}
            css="clearboth pa:10 flex">
            <View css="he:30 zi:1">
              <View
                invertColor={false}
                css="bor:5 zi:1 to:5 wi:40 he:15 juc:center ali:center absolute le:45%">
                <TouchableOpacity
                  onPress={() => {
                    (onHide || setIsV)(false);
                  }}
                  css="clearboth flex juc:center ali:center">
                  <Icon
                    css="bold"
                    type="Entypo"
                    name="chevron-small-down"
                    size={14}
                    invertColor={false}
                  />
                </TouchableOpacity>
              </View>
              <Text
                ifTrue={title != undefined}
                invertColor={true}
                css="header fos:18 bold co:white clearwidth flex">
                {title}
              </Text>
            </View>
            <View css="flex zi:5 maw:99%">
              {onReady ? children : null}
            </View>
          </View>
        </Animated.View>
      </>,
      id.current,
      {
        visible: isV,
        toTop
      }
    );
  });

  return null;
};



0

There are 0 best solutions below