在反应本机导航屏幕之间切换时,PanGesture动画不起作用

时间:2020-03-30 12:19:27

标签: javascript react-native react-native-navigation react-native-animatable

我已按照本教程https://reactnavigation.org/docs/bottom-tab-navigator#tabbar使用react native导航创建了自定义标签栏组件。然后,我在导航顶部放置了一个自定义组件,以模仿Spotify最小化播放器,以便在我可以执行从最小化播放器到全尺寸播放器的拖动手势时。但是,当我运行该应用程序时,该手势在第一个屏幕上效果很好,但是当我切换屏幕时,该手势不起作用https://www.youtube.com/watch?v=w-8NSQyWPHI&feature=youtu.be

Custom Tab Bar

import React, { Fragment, useState } from 'react';
import { Text, View, TouchableOpacity, Dimensions, StyleSheet } from 'react-native'
import { PanGestureHandler, State } from 'react-native-gesture-handler'
import { clamp, onGestureEvent, timing, withSpring } from 'react-native-redash'
import Animated from 'react-native-reanimated'
import { getBottomSpace } from 'react-native-iphone-x-helper'
import Player from './player/Player'
import MiniPlayer from './player/MiniPlayer'

const { height } = Dimensions.get('window')
const TABBAR_HEIGHT = getBottomSpace() + 50
const MINIMIZED_PLAYER_HEIGHT = 52
const SNAP_TOP = 0
const SNAP_BOTTOM = height - TABBAR_HEIGHT - MINIMIZED_PLAYER_HEIGHT

const config = {
  damping: 30,
  mass: 1,
  stiffness: 150,
  overshootClamping: false,
  restSpeedThreshold: 0.1,
  restDisplacementThreshold: 0.1
}

const {
  Clock,
  Value,
  cond,
  useCode,
  set,
  block,
  not,
  clockRunning,
  interpolate,
  diffClamp,
  Extrapolate
} = Animated

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  playerSheet: {
    ...StyleSheet.absoluteFillObject,
    backgroundColor: 'white'
  }
})


export default ({ statee, descriptors, navigation }) => {

  const translationY = new Value(0)
  const velocityY = new Value(0)
  const state = new Value(State.UNDETERMINED)
  const offset = new Value(SNAP_BOTTOM)
  const goUp: Animated.Value<0 | 1> = new Value(0)
  const goDown: Animated.Value<0 | 1> = new Value(0)

  const gestureHandler = onGestureEvent({
    state,
    translationY,
    velocityY
  })

  const translateY = clamp(
    withSpring({
      state,
      offset,
      value: translationY,
      velocity: velocityY,
      snapPoints: [SNAP_TOP, SNAP_BOTTOM],
      config
    }),
    SNAP_TOP,
    SNAP_BOTTOM
  )

  const translateBottomTab = interpolate(translateY, {
    inputRange: [SNAP_TOP, SNAP_BOTTOM],
    outputRange: [TABBAR_HEIGHT, 0],
    extrapolate: Extrapolate.CLAMP
  })

  const opacity = interpolate(translateY, {
    inputRange: [SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT, SNAP_BOTTOM],
    outputRange: [0, 1],
    extrapolate: Extrapolate.CLAMP
  })

  const opacity2 = interpolate(translateY, {
    inputRange: [
      SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT * 2,
      SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT
    ],
    outputRange: [0, 1],
    extrapolate: Extrapolate.CLAMP
  })

  const clock = new Clock()

  useCode(
    block([
      cond(goUp, [
        set(
          offset,
          timing({
            clock,
            from: offset,
            to: SNAP_TOP
          })
        ),
        cond(not(clockRunning(clock)), [set(goUp, 0)])
      ]),
      cond(goDown, [
        set(
          offset,
          timing({
            clock,
            from: offset,
            to: SNAP_BOTTOM
          })
        ),
        cond(not(clockRunning(clock)), [set(goDown, 0)])
      ])
    ]),
    []
  )

  goUpHandler = () => {
    console.log('TAPPERD UP');

    goUp.setValue(1)
  }

  goDownHandler = () => {
    console.log('TAPPERD DOWN');

    goDown.setValue(1)
  }

  return (
    <>
      <PanGestureHandler {...gestureHandler}>
        <Animated.View
          style={[styles.playerSheet, { transform: [{ translateY }] }]}
        >
          <Player onPress={this.goDownHandler} />
          <Animated.View
            pointerEvents='none'
            style={{
              opacity: opacity2,
              backgroundColor: 'white',
              ...StyleSheet.absoluteFillObject
            }}
          />
          <Animated.View style={{
            opacity: opacity,
            position: 'absolute',
            top: 0,
            left: 0,
            right: 0,
            height: MINIMIZED_PLAYER_HEIGHT
          }}>
            <MiniPlayer onPress={this.goUpHandler} />
          </Animated.View>
        </Animated.View>
      </PanGestureHandler>

      <Animated.View style={{
        flexDirection: 'row',
        height: TABBAR_HEIGHT,
        paddingTop: 8,
        justifyContent: 'center',
        backgroundColor: 'white',
        transform: [{ translateY: translateBottomTab }]
      }}>
        {statee.routes.map((route, index) => {
          const { options } = descriptors[route.key];
          const label =
            options.tabBarLabel !== undefined
              ? options.tabBarLabel
              : options.title !== undefined
                ? options.title
                : route.name;

          const Icon = options.tabBarIcon
          const isFocused = statee.index === index;

          const onPress = () => {
            const event = navigation.emit({
              type: 'tabPress',
              target: route.key,
              canPreventDefault: true,
            });

            if (!isFocused && !event.defaultPrevented) {
              navigation.navigate(route.name);
            }
          };

          const onLongPress = () => {
            navigation.emit({
              type: 'tabLongPress',
              target: route.key,
            });
          };

          return (
            <Animated.View style={styles.container} key={index}>
              <Animated.View style={{
                alignItems: 'center',
                justifyContent: 'center'
              }}>
                <TouchableOpacity
                  accessibilityRole="button"
                  accessibilityStates={isFocused ? ['selected'] : []}
                  accessibilityLabel={options.tabBarAccessibilityLabel}
                  testID={options.tabBarTestID}
                  onPress={onPress}
                  onLongPress={onLongPress}
                >
                  <Icon />
                </TouchableOpacity>
              </Animated.View>
            </Animated.View>
          );
        })}
      </Animated.View>
    </>
  );
}

1 个答案:

答案 0 :(得分:0)

我有确切的问题。这是因为reanimated在重新渲染时被弄乱了。

我通过将以下代码移出功能/类组件使其全局来解决此问题。

const translationY = new Value(0)
  const velocityY = new Value(0)
  const state = new Value(State.UNDETERMINED)
  const offset = new Value(SNAP_BOTTOM)
  const goUp: Animated.Value<0 | 1> = new Value(0)
  const goDown: Animated.Value<0 | 1> = new Value(0)

  const gestureHandler = onGestureEvent({
    state,
    translationY,
    velocityY
  })

  const translateY = clamp(
    withSpring({
      state,
      offset,
      value: translationY,
      velocity: velocityY,
      snapPoints: [SNAP_TOP, SNAP_BOTTOM],
      config
    }),
    SNAP_TOP,
    SNAP_BOTTOM
  )

  const translateBottomTab = interpolate(translateY, {
    inputRange: [SNAP_TOP, SNAP_BOTTOM],
    outputRange: [TABBAR_HEIGHT, 0],
    extrapolate: Extrapolate.CLAMP
  })

  const opacity = interpolate(translateY, {
    inputRange: [SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT, SNAP_BOTTOM],
    outputRange: [0, 1],
    extrapolate: Extrapolate.CLAMP
  })

  const opacity2 = interpolate(translateY, {
    inputRange: [
      SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT * 2,
      SNAP_BOTTOM - MINIMIZED_PLAYER_HEIGHT
    ],
    outputRange: [0, 1],
    extrapolate: Extrapolate.CLAMP
  })

  const clock = new Clock()

  useCode(
    block([
      cond(goUp, [
        set(
          offset,
          timing({
            clock,
            from: offset,
            to: SNAP_TOP
          })
        ),
        cond(not(clockRunning(clock)), [set(goUp, 0)])
      ]),
      cond(goDown, [
        set(
          offset,
          timing({
            clock,
            from: offset,
            to: SNAP_BOTTOM
          })
        ),
        cond(not(clockRunning(clock)), [set(goDown, 0)])
      ])
    ]),
    []
  )