react-native-gesture-handler#PanGestureHandler JavaScript Examples

The following examples show how to use react-native-gesture-handler#PanGestureHandler. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.js    From discovery-mobile-ui with MIT License 6 votes vote down vote up
Catalog = ({
  collection, selectedRecordsGroupedByType, navigation, timelineIntervals,
}) => {
  const noRecords = timelineIntervals.recordCount === 0;
  return (
    <PanGestureHandler
      activeOffsetX={-10}
      failOffsetX={[-20, 0]}
      onGestureEvent={() => navigation.navigate('CollectionDetails')}
    >
      <View style={styles.drawerContainer}>
        <FilterDrawer>
          <CatalogScreenHeader collection={collection} navigation={navigation} />
          <Timeline noRecords={noRecords} />
          {noRecords && (
          <View style={styles.zeroStateContainer}>
            <Text style={styles.zeroStateText}>
              No Records available based on the Filters or the time interval.
            </Text>
          </View>
          )}
          {!noRecords && (
          <>
            <ResourceTypePicker />
            <ScrollView style={styles.scrollView}>
              <SubTypeAccordionsContainer data={selectedRecordsGroupedByType} />
            </ScrollView>
          </>
          )}
        </FilterDrawer>
      </View>
    </PanGestureHandler>
  );
}
Example #2
Source File: BottomSheet.js    From actual with MIT License 5 votes vote down vote up
render() {
    const { children, flush } = this.props;

    return (
      <View style={styles.container} ref={el => (this.container = el)}>
        <PanGestureHandler
          ref={this.drawerHandler}
          onGestureEvent={this.panEvent}
          onHandlerStateChange={this.onHandlerStateChange}
        >
          <Animated.View style={{ flex: 1 }}>
            <Animated.View
              style={[
                styles.darkened,
                {
                  backgroundColor: 'rgba(0, 0, 0, 1.0)',
                  opacity: this.translateY.interpolate({
                    inputRange: [0, windowHeight],
                    outputRange: [0.5, 0]
                  })
                }
              ]}
            />

            <Animated.View
              style={{
                flex: 1,
                borderTopLeftRadius: 4,
                borderTopRightRadius: 4,
                backgroundColor: '#f0f0f0',
                opacity: this.opacity,
                transform: [
                  {
                    translateY: this.translateY.interpolate({
                      inputRange: [0, windowHeight],
                      outputRange: [0, windowHeight],
                      extrapolate: 'clamp'
                    })
                  }
                ]
              }}
            >
              <View style={[styles.content, !flush && { paddingTop: 15 }]}>
                {typeof children === 'function'
                  ? children({
                      panEvent: this.panEvent,
                      onHandlerStateChange: this.onHandlerStateChange
                    })
                  : children}
              </View>
              <View style={styles.header}>
                <View style={styles.closeHandle} />
              </View>
            </Animated.View>
          </Animated.View>
        </PanGestureHandler>
      </View>
    );
  }
Example #3
Source File: ScrollableBottomSheet.js    From actual with MIT License 5 votes vote down vote up
render() {
    const { shouldCheckScrollPan, forcedHeight } = this.state;
    const {
      renderHeader,
      renderScroll = this.defaultRenderScroll
    } = this.props;

    return (
      <BottomSheet
        ref={el => (this.sheet = el)}
        onMove={this.onMove}
        {...this.props}
      >
        {({ panEvent, onHandlerStateChange }) => {
          return (
            <View style={{ flex: 1 }}>
              {renderHeader && renderHeader()}
              <PanGestureHandler
                ref={this.checkScrollHandler}
                enabled={shouldCheckScrollPan}
                activeOffsetY={20}
                failOffsetY={[-1, 10000]}
                onGestureEvent={panEvent}
                onHandlerStateChange={onHandlerStateChange}
                simultaneousHandlers={this.scrollWrapperHandler}
              >
                <Animated.View style={{ flex: 1 }}>
                  <PanGestureHandler
                    ref={this.scrollWrapperHandler}
                    simultaneousHandlers={[
                      this.scrollHandler,
                      this.checkScrollHandler
                    ]}
                    onGestureEvent={this.onScrollPan}
                  >
                    <View
                      style={
                        forcedHeight
                          ? { flex: 1, paddingBottom: forcedHeight }
                          : { flex: 1 }
                      }
                    >
                      <NativeViewGestureHandler
                        ref={this.scrollHandler}
                        simultaneousHandlers={this.scrollWrapperHandler}
                        {...(shouldCheckScrollPan
                          ? { waitFor: this.checkScrollHandler }
                          : {})}
                      >
                        {renderScroll({
                          ref: el => (this.scrollView = el),
                          automaticallyAdjustContentInsets: false,
                          onScrollBeginDrag: this.onScrollBeginDrag,
                          onScrollEndDrag: this.onScrollEndDrag,
                          onScroll: this.onScroll,
                          onMomentumScrollEnd: this.onMomentumScrollEnd,
                          scrollEventThrottle: 1
                        })}
                      </NativeViewGestureHandler>
                    </View>
                  </PanGestureHandler>
                </Animated.View>
              </PanGestureHandler>
            </View>
          );
        }}
      </BottomSheet>
    );
  }
Example #4
Source File: budget.js    From actual with MIT License 4 votes vote down vote up
render() {
    const {
      type,
      categoryGroups,
      month,
      monthBounds,
      editMode,
      refreshControl,
      onPrevMonth,
      onNextMonth,
      onAddCategory,
      onReorderCategory,
      onReorderGroup,
      onShowBudgetDetails,
      onOpenActionSheet,
      onBudgetAction
    } = this.props;
    let { editingCategory } = this.state;
    let currentMonth = monthUtils.currentMonth();

    return (
      <NamespaceContext.Provider value={monthUtils.sheetForMonth(month, type)}>
        <View
          style={{ flex: 1, backgroundColor: 'white' }}
          data-testid="budget-table"
        >
          <BudgetHeader
            currentMonth={month}
            monthBounds={monthBounds}
            editMode={editMode}
            onDone={() => this.props.onEditMode(false)}
            onOpenActionSheet={onOpenActionSheet}
            onPrevMonth={onPrevMonth}
            onNextMonth={onNextMonth}
          />
          <View
            style={{
              flexDirection: 'row',
              paddingHorizontal: 10,
              paddingVertical: 10,
              paddingRight: 14,
              backgroundColor: 'white',
              borderBottomWidth: 1,
              borderColor: colors.n9
            }}
          >
            {type === 'report' ? (
              <Saved projected={month >= currentMonth} />
            ) : (
              <ToBudget
                toBudget={rolloverBudget.toBudget}
                onPress={onShowBudgetDetails}
              />
            )}
            <View style={{ flex: 1, zIndex: -1 }} />

            <View style={{ width: 90 }}>
              <Label title="BUDGETED" style={{ color: colors.n1 }} />
              <CellValue
                binding={reportBudget.totalBudgetedExpense}
                type="financial"
                style={[
                  styles.smallText,
                  { color: colors.n1, textAlign: 'right', fontWeight: '500' }
                ]}
                formatter={(value) => {
                  return format(-parseFloat(value || '0'), 'financial');
                }}
              />
            </View>
            <View style={{ width: 90 }}>
              <Label title="BALANCE" style={{ color: colors.n1 }} />
              <CellValue
                binding={rolloverBudget.totalBalance}
                type="financial"
                style={[
                  styles.smallText,
                  { color: colors.n1, textAlign: 'right', fontWeight: '500' }
                ]}
              />
            </View>
          </View>

          <AndroidKeyboardAvoidingView includeStatusBar={true}>
            {!editMode ? (
              <ScrollView
                ref={(el) => (this.list = el)}
                keyboardShouldPersistTaps="always"
                refreshControl={refreshControl}
                style={{ backgroundColor: colors.n10 }}
                automaticallyAdjustContentInsets={false}
              >
                <BudgetGroups
                  type={type}
                  categoryGroups={categoryGroups}
                  editingId={editingCategory}
                  editMode={editMode}
                  gestures={this.gestures}
                  month={month}
                  onEditCategory={this.onEditCategory}
                  onAddCategory={onAddCategory}
                  onReorderCategory={onReorderCategory}
                  onReorderGroup={onReorderGroup}
                  onBudgetAction={onBudgetAction}
                />
              </ScrollView>
            ) : (
              <DragDrop>
                {({
                  dragging,
                  onGestureEvent,
                  onHandlerStateChange,
                  scrollRef,
                  onScroll
                }) => (
                  <NativeViewGestureHandler
                    enabled={!dragging}
                    waitFor={this.gestures.pan}
                    ref={this.gestures.scroll}
                  >
                    <Animated.ScrollView
                      ref={(el) => {
                        scrollRef.current = el;
                        this.list = el;
                      }}
                      onScroll={onScroll}
                      keyboardShouldPersistTaps="always"
                      scrollEventThrottle={16}
                      scrollEnabled={!dragging}
                      style={{ backgroundColor: colors.n10 }}
                    >
                      <PanGestureHandler
                        avgTouches
                        minDeltaX={2}
                        minDeltaY={2}
                        maxPointers={1}
                        onGestureEvent={onGestureEvent}
                        onHandlerStateChange={onHandlerStateChange}
                        ref={this.gestures.pan}
                        waitFor={this.gestures.scroll}
                      >
                        <Animated.View>
                          <BudgetGroups
                            categoryGroups={categoryGroups}
                            editingId={editingCategory}
                            editMode={editMode}
                            gestures={this.gestures}
                            onEditCategory={this.onEditCategory}
                            onAddCategory={onAddCategory}
                            onReorderCategory={onReorderCategory}
                            onReorderGroup={onReorderGroup}
                          />
                        </Animated.View>
                      </PanGestureHandler>

                      <DragDropHighlight />
                    </Animated.ScrollView>
                  </NativeViewGestureHandler>
                )}
              </DragDrop>
            )}
          </AndroidKeyboardAvoidingView>
        </View>
      </NamespaceContext.Provider>
    );
  }
Example #5
Source File: ChildEdit.js    From actual with MIT License 4 votes vote down vote up
render() {
    const { transaction, exiting, getCategoryName, amountSign } = this.props;

    return (
      <View
        style={{
          position: 'absolute',
          top: 0,
          left: 0,
          bottom: 0,
          right: 0,
          justifyContent: 'flex-end'
        }}
        pointerEvents={exiting ? 'none' : 'auto'}
      >
        <Animated.View
          style={{
            position: 'absolute',
            top: 0,
            left: 0,
            bottom: 0,
            right: 0,
            backgroundColor: 'black',
            opacity: this.gestureY.interpolate({
              inputRange: [0, 400],
              outputRange: [0.4, 0],
              extrapolate: 'clamp'
            })
          }}
        />
        <PanGestureHandler
          onGestureEvent={this.gestureEvent}
          onHandlerStateChange={this.onHandlerStateChange}
        >
          <Animated.View
            style={{
              justifyContent: 'flex-end',
              backgroundColor: 'white',
              margin: 10,
              borderRadius: 4,
              overflow: 'hidden',
              transform: [{ translateY: this.gestureY }]
            }}
          >
            <View
              style={{
                borderBottomWidth: 1,
                borderColor: '#f0f0f0',
                flexDirection: 'row',
                justifyContent: 'center',
                padding: 15
              }}
            >
              <Text style={mobileStyles.header.headerTitleStyle}>
                Edit Split Transaction
              </Text>
            </View>
            <View
              style={{
                backgroundColor: '#FBFCFC'
              }}
            >
              <View style={{ alignItems: 'center', marginVertical: 15 }}>
                <FieldLabel
                  title="Amount"
                  flush
                  style={{ marginBottom: 0, paddingLeft: 0 }}
                />
                <FocusableAmountInput
                  value={transaction.amount}
                  sign={amountSign}
                  style={{ height: 26, transform: [] }}
                  focusedStyle={{
                    width: 'auto',
                    paddingVertical: 0,
                    paddingHorizontal: 10,
                    minWidth: 100,
                    transform: [{ translateY: -0.5 }]
                  }}
                  onBlur={value =>
                    this.props.onEdit(transaction, 'amount', value.toString())
                  }
                  textStyle={{ fontSize: 20, textAlign: 'center' }}
                />
              </View>

              <FieldLabel title="Category" flush />
              <TapField
                value={getCategoryName(transaction.category)}
                onTap={this.onEditCategory}
              />

              <FieldLabel title="Notes" />
              <InputField
                defaultValue={transaction.notes}
                multiline={true}
                numberOfLines={10}
                style={{ height: 60, marginBottom: 25 }}
                paddingTop={10}
                paddingBottom={10}
                onUpdate={value =>
                  this.props.onEdit(transaction, 'notes', value)
                }
              />
            </View>
          </Animated.View>
        </PanGestureHandler>
      </View>
    );
  }
Example #6
Source File: PatternLock.js    From react-native-pattern-lock with MIT License 4 votes vote down vote up
export function PatternLock(props) {
  const [isError, setIsError] = useState(false);
  const canTouch = useSharedValue(true);
  const patternPoints = useSharedValue();
  const selectedIndexes = useSharedValue([]);
  const endPoint = useSharedValue();
  const containerLayout = useSharedValue({ width: 0, height: 0, min: 0 });
  const R = useDerivedValue(
    () =>
      (containerLayout.value.min / props.rowCount - props.patternMargin * 2) / 2
  );
  const cvc = useAnimatedStyle(() => ({
    flexDirection: "row",
    flexWrap: "wrap",
    marginBottom: `${
      Math.max(
        0,
        containerLayout.value.height / containerLayout.value.width - 1.25
      ) * 50
    }%`,
    width: containerLayout.value.min,
    height: containerLayout.value.min,
  }));
  const msgX = useSharedValue(0);
  const msgColor = { color: isError ? props.errorColor : props.activeColor };
  const msgStyle = useAnimatedStyle(() => {
    return { transform: [{ translateX: msgX.value }] };
  });
  const onContainerLayout = ({
    nativeEvent: {
      layout: { x, y, width, height },
    },
  }) =>
    (containerLayout.value = {
      width,
      height,
      min: Math.min(width, height),
    });
  const onPatternLayout = ({ nativeEvent: { layout } }) => {
    const points = [];
    for (let i = 0; i < props.rowCount; i++) {
      for (let j = 0; j < props.columnCount; j++) {
        points.push({
          x: layout.x + (layout.width / props.columnCount) * (j + 0.5),
          y: layout.y + (layout.height / props.rowCount) * (i + 0.5),
        });
      }
    }
    patternPoints.value = points;
  };
  const onEndJS = (res) => {
    if (props.onCheck) {
      canTouch.value = false;
      if (!props.onCheck(res)) {
        setIsError(true);
        const closeError = () => setIsError(false);
        runOnUI(() => {
          cancelAnimation(msgX);
          //修复iOS上原地spring不动的问题。
          msgX.value = withSpring(
            msgX.value === 0 ? 0.1 : 0,
            {
              stiffness: 2000,
              damping: 10,
              mass: 1,
              velocity: 2000,
            },
            (finished) => {
              runOnJS(closeError)();
              canTouch.value = true;
              selectedIndexes.value = [];
            }
          );
        })();
      } else {
        setIsError(false);
        setTimeout(() => {
          selectedIndexes.value = [];
          canTouch.value = true;
        }, 1000);
      }
    }
  };
  const panHandler = useAnimatedGestureHandler({
    onStart: (evt) => {
      if (
        canTouch.value &&
        patternPoints.value &&
        selectedIndexes.value.length === 0
      ) {
        const selected = [];
        patternPoints.value.every((p, idx) => {
          if (
            (p.x - evt.x) * (p.x - evt.x) + (p.y - evt.y) * (p.y - evt.y) <
            R.value * R.value
          ) {
            selected.push(idx);
            return false;
          }
          return true;
        });
        selectedIndexes.value = selected;
      }
    },
    onActive: (evt) => {
      if (
        canTouch.value &&
        patternPoints.value &&
        selectedIndexes.value.length > 0
      ) {
        patternPoints.value.every((p, idx) => {
          if (
            (p.x - evt.x) * (p.x - evt.x) + (p.y - evt.y) * (p.y - evt.y) <
            R.value * R.value
          ) {
            if (selectedIndexes.value.indexOf(idx) < 0) {
              selectedIndexes.value = [...selectedIndexes.value, idx];
            }
            return false;
          }
          return true;
        });
        endPoint.value = { x: evt.x, y: evt.y };
      }
    },
    onEnd: (evt) => {
      if (!canTouch.value) return;
      endPoint.value = null;
      if (selectedIndexes.value.length > 0)
        runOnJS(onEndJS)(selectedIndexes.value.join(""));
    },
  });
  const animatedProps = useAnimatedProps(() => {
    let d = "";
    selectedIndexes.value.forEach((idx) => {
      d += !d ? " M" : " L";
      d += ` ${patternPoints.value[idx].x},${patternPoints.value[idx].y}`;
    });
    if (d && endPoint.value) d += ` L${endPoint.value.x},${endPoint.value.y}`;
    if (!d) d = "M-1,-1";
    return { d };
  });

  return (
    <PanGestureHandler onGestureEvent={panHandler}>
      <Animated.View style={styles.container} onLayout={onContainerLayout}>
        <TapGestureHandler onGestureEvent={panHandler}>
          <Animated.View style={styles.container}>
            <View style={styles.msgc}>
              <Animated.Text style={[msgColor, msgStyle]}>
                {props.message}
              </Animated.Text>
            </View>
            <Animated.View style={cvc} onLayout={onPatternLayout}>
              {Array(props.rowCount * props.columnCount)
                .fill(0)
                .map((_, idx) => {
                  const patternColor = useDerivedValue(() => {
                    if (selectedIndexes.value.findIndex((v) => v === idx) < 0) {
                      return props.inactiveColor;
                    } else if (isError) {
                      return props.errorColor;
                    } else {
                      return props.activeColor;
                    }
                  });
                  const outer = useAnimatedStyle(() => {
                    return {
                      borderWidth: 2,
                      width: 2 * R.value,
                      height: 2 * R.value,
                      alignItems: "center",
                      justifyContent: "center",
                      borderColor: patternColor.value,
                      borderRadius: 2 * R.value,
                      margin: props.patternMargin,
                    };
                  });
                  const inner = useAnimatedStyle(() => {
                    return {
                      width: R.value * 0.8,
                      height: R.value * 0.8,
                      borderRadius: R.value * 0.8,
                      backgroundColor: patternColor.value,
                    };
                  });
                  return (
                    <Animated.View key={idx} style={outer}>
                      <Animated.View style={inner} />
                    </Animated.View>
                  );
                })}
            </Animated.View>
            <Svg style={styles.svg} width="100%" height="100%">
              <AnimatedPath
                fill="none"
                strokeWidth={3}
                animatedProps={animatedProps}
                stroke={isError ? props.errorColor : props.activeColor}
              />
            </Svg>
          </Animated.View>
        </TapGestureHandler>
      </Animated.View>
    </PanGestureHandler>
  );
}
Example #7
Source File: index.js    From interface-nubank with MIT License 4 votes vote down vote up
export default function Main() {
  let offset = 0;
  const translateY = new Animated.Value(0);

  const animatedEvent = Animated.event(
    [
      {
        nativeEvent: {
          translationY: translateY,
        },
      },
    ],
    { useNativeDriver: true },
  );

  function onHandlerStateChanged(event) {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      let opened = false;
      const { translationY } = event.nativeEvent;

      offset += translationY;

      if (translationY >= 100) {
        opened = true;
      } else {
        translateY.setValue(offset);
        translateY.setOffset(0);
        offset = 0;
      }

      Animated.timing(translateY, {
        toValue: opened ? 380 : 0,
        duration: 200,
        useNativeDriver: true,
      }).start(() => {
        offset = opened ? 380 : 0;
        translateY.setOffset(offset);
        translateY.setValue(0);
      });
    }
  }

  return (
    <SafeAreaView>
      <Container>
        <Header />
        <Content>
          <Menu translateY={translateY} />

          <PanGestureHandler
            onGestureEvent={animatedEvent}
            onHandlerStateChange={onHandlerStateChanged}
          >
            <Card style={{
              transform: [{
                translateY: translateY.interpolate({
                  inputRange: [-350, 0, 380],
                  outputRange: [-50, 0, 380],
                  extrapolate: 'clamp',
                }),
              }],
            }}
            >
              <CardHeader>
                <Icon name="attach-money" size={28} color="#666" />
                <Icon name="visibility-off" size={28} color="#666" />
              </CardHeader>
              <CardContent>
                <Title>Saldo disponível</Title>
                <Description>R$ 197.611,65</Description>
              </CardContent>
              <CardFooter>
                <Annotation>
                Transferência de R$ 20,00 recebida de Diego Schell Fernandes hoje às 06:00h
                </Annotation>
              </CardFooter>
            </Card>
          </PanGestureHandler>

        </Content>

        <Tabs translateY={translateY} />
      </Container>
    </SafeAreaView>
  );
}
Example #8
Source File: index.js    From openweathermap-reactnative with MIT License 4 votes vote down vote up
export default function ForecastDetails() {
  let offset = 0;
  const translateY = new Animated.Value(0);

  const animatedEvent = Animated.event(
    [
      {
        nativeEvent: {
          translationY: translateY,
        },
      },
    ],
    {useNativeDriver: true},
  );

  function onHandlerStateChanged(event) {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      const {translationY} = event.nativeEvent;
      let opened = false;
      offset += translationY;

      if (translationY >= 100) {
        opened = true;
      } else {
        translateY.setValue(offset);
        translateY.setOffset(0);
        offset = 0;
      }

      Animated.timing(translateY, {
        toValue: opened ? 500 : 0,
        duration: 300,
        useNativeDriver: true,
      }).start(() => {
        offset = opened ? 500 : 0;
        translateY.setOffset(offset);
        translateY.setValue(0);
      });
    }
  }

  return (
    <PanGestureHandler
      onGestureEvent={animatedEvent}
      onHandlerStateChange={onHandlerStateChanged}>
      <Card
        style={{
          transform: [
            {
              translateY: translateY.interpolate({
                inputRange: [-300, 0, 500],
                outputRange: [-30, 0, 620],
                extrapolate: 'clamp',
              }),
            },
          ],
        }}>
        <Header
          style={{
            opacity: translateY.interpolate({
              inputRange: [0, 400],
              outputRange: [1, 0],
              extrapolate: 'clamp',
            }),
          }}>
          Vreeland
        </Header>
        <Body
          style={{
            opacity: translateY.interpolate({
              inputRange: [0, 400],
              outputRange: [1, 0],
              extrapolate: 'clamp',
            }),
          }}>
          <WeatherInfoContainer>
            <Wind>M7º / L5º</Wind>
            <Temperature>5º</Temperature>
            <WeatherInfo>Light rain</WeatherInfo>
            <Umidity>87 %</Umidity>
          </WeatherInfoContainer>
          <AnimationContainer>
            <LottieView source={LottieAnimationJson} autoPlay loop />
          </AnimationContainer>
        </Body>
        <Bottom>
          <Observations>
            Right now is 5ºC and feels like -1ºC outside. The wind is blowing
            around 8.7 km/h and the pressure is 1009 hPa.
          </Observations>
          <BookmarkButton>
            <ButtonText>Bookmark this location</ButtonText>
          </BookmarkButton>
        </Bottom>
      </Card>
    </PanGestureHandler>
  );
}