android - 带有 React Native 的 Google map 面板

标签 android reactjs google-maps react-native swipe-gesture

我正在尝试复制 Google map 滑动面板(“探索伦敦市”)或类似的东西。面板应通过滑动打开和关闭,打开时,内部内容应滚动,即在 ScrollView 内,然后当 ScrollView 位于顶部时,滚动被禁用,面板可以关闭。

enter image description here

目前,我正在使用 rn-sliding-up-panel 插件进行面板滑动,并带有 ScrollView 的子组件。然后我找到 ScrollView 的位置,如果它位于 0 并且用户向下滑动,我关闭面板。

但是,这在 Android 和 iOS 设备上似乎都有很多错误。有时它会停留在 ScrollView 中的位置 0。

有人成功构建过类似的东西吗?也许我走在正确的轨道上,但需要改进?或者有更好的插件吗?

任何建议或示例将不胜感激。

最佳答案

class ssss extends Component {
  constructor(props) {
    super(props);
    this.state = {
      position: new Animated.Value(props.isOpen ? 0 : height),
      opacity: new Animated.Value(0),
      height: defaultHeight,
      expanded: false,
      visible: props.isOpen
    };
  }

  // When user starts pulling popup previous height gets stored here
  // to help us calculate new height value during and after pulling
  _previousHeight = 0;

  componentDidMount() {
    // Initialize PanResponder to handle move gestures
    this._panResponder = PanResponder.create({
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponder: (evt, gestureState) => {
        const { dx, dy } = gestureState;
        // Ignore taps
        if (dx !== 0 && dy === 0) {
          return true;
        }
        return false;
      },
      onPanResponderGrant: (evt, gestureState) => {
        // Store previous height before user changed it
        this._previousHeight = this.state.height;
      },
      onPanResponderMove: (evt, gestureState) => {
        // Pull delta and velocity values for y axis from gestureState
        const { dy, vy } = gestureState;
        // Subtract delta y from previous height to get new height
        let newHeight = this._previousHeight - dy;

        // Animate heigh change so it looks smooth
        LayoutAnimation.easeInEaseOut();

        // Switch to expanded mode if popup pulled up above 80% mark
        if (newHeight > 600 - 600 / 5) {
          this.setState({ expanded: true });
        } else {
          this.setState({ expanded: false });
        }

        // Expand to full height if pulled up rapidly
        if (vy < -0.75) {
          this.setState({
            expanded: true,
            height: height * 0.65
          });
        }

        // Close if pulled down rapidly
        else if (vy > 0.75) {
          this.props.onClose();
        }
        // Close if pulled below 95% mark of default height
        else if (newHeight < defaultHeight * 0.95) {
          this.props.onClose();
        }
        // Limit max height to screen height
        else if (newHeight > 600) {
          this.setState({ height: height * 0.65 });
        } else {
          this.setState({ height: newHeight });
        }
      },
      onPanResponderTerminationRequest: (evt, gestureState) => true,
      onPanResponderRelease: (evt, gestureState) => {
        const { dy } = gestureState;
        const newHeight = this._previousHeight - dy;

        // Close if pulled below default height
        if (newHeight < defaultHeight) {
          this.props.onClose();
        }

        // Update previous height
        this._previousHeight = this.state.height;
      },
      onShouldBlockNativeResponder: (evt, gestureState) => {
        // Returns whether this component should block native components from becoming the JS
        // responder. Returns true by default. Is currently only supported on android.
        return true;
      }
    });
  }

  // Handle isOpen changes to either open or close popup
  componentWillReceiveProps(nextProps) {
    // isOpen prop changed to true from false
    if (!this.props.isOpen && nextProps.isOpen) {
      this.animateOpen();
    }
    // isOpen prop changed to false from true
    else if (this.props.isOpen && !nextProps.isOpen) {
      this.animateClose();
    }
  }

  // Open popup
  animateOpen() {
    // Update state first
    this.setState({ visible: true }, () => {
      Animated.parallel([
        // Animate opacity
        Animated.timing(
          this.state.opacity,
          { toValue: 0.5 } // semi-transparent
        ),
        // And slide up
        Animated.timing(
          this.state.position,
          { toValue: 0 } // top of the screen
        )
      ]).start();
    });
  }

  // Close popup
  animateClose() {
    Animated.parallel([
      // Animate opacity
      Animated.timing(
        this.state.opacity,
        { toValue: 0 } // transparent
      ),
      // Slide down
      Animated.timing(
        this.state.position,
        { toValue: height } // bottom of the screen
      )
    ]).start(() =>
      this.setState({
        // Reset to default values
        height: defaultHeight,
        expanded: false,
        visible: false
      })
    );
  }

  render() {
    // Render nothing if not visible
    if (!this.state.visible) {
      return null;
    }
    return (
      <View style={styles.container}>
        {/* Closes popup if user taps on semi-transparent backdrop */}
        <TouchableWithoutFeedback onPress={this.props.onClose}>
          <Animated.View
            style={[styles.backdrop, { opacity: this.state.opacity }]}
          />
        </TouchableWithoutFeedback>
        <Animated.View
          style={[
            styles.modal,
            {
              // Animates height
              height: this.state.height,
              // Animates position on the screen
              transform: [
                { translateY: this.state.position },
                { translateX: 0 }
              ]
            }
          ]}
        >
          {/* Content */}
          <View style={styles.content}>
            <View
              style={[styles.topUpContainer]}
              {...this._panResponder.panHandlers}
            >
              <View style={styles.hookerContainer}>
                <View style={styles.hooker} />
              </View>
              {/* Your content comes here */}
            </View>
          </View>
        </Animated.View>
      </View>
    );
  }
}

这可能对你有帮助

关于android - 带有 React Native 的 Google map 面板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54182772/

相关文章:

javascript - Google Maps API,添加带有标签的自定义 SVG 标记

android - 如何在android中使用方位平滑地旋转 map View

google-maps - 有没有办法使用 vue2-google-maps 和 marker-clusterer-plus 来判断哪些标记正在被聚类

java - 在我编辑小部件 xml 后,eclipse 拒绝运行我的 android 项目

java - 无法获得 gomobile 绑定(bind)以使用 java

javascript - ReactJS AXIOS 帖子不起作用

javascript - React Redux 追加组件

android - 无法解析 Firebase 身份验证 15.0.0

android - 如何将 Three.js 代码实现到 Android 移动应用程序中?

reactjs - 如何增加 react-native Slider 组件的高度?