【问题标题】:react-native : change view corresponding to scroll positionreact-native : 根据滚动位置更改视图
【发布时间】:2020-04-21 15:15:54
【问题描述】:

我正在使用Animation.view 来更改headerheightbackground

我将heightbackground 设置如下:

const HeaderHeight = this.state.scrollY.interpolate({
      inputRange:[0, Header_Max_Height - Header_Min_Height],
      outputRange:[Header_Max_Height, Header_Min_Height],
      extrapolate:'clamp'
    })

const AnimateHeaderBackgroundColor = this.state.scrollY.interpolate({
        inputRange: [ 0, ( Header_Max_Height - Header_Min_Height )  ],
        outputRange: [ '#009688', '#00BCD4' ],
        extrapolate: 'clamp'
    })

这是我的动画视图。

<Animated.View style={{width:'100%', height: HeaderHeight, backgroundColor:AnimateHeaderBackgroundColor}}></Animated.View>

一切正常。

我的问题有没有办法像heightbackgroundcolor 一样更改视图?

例如,假设我有两个视图:

//view1
<View style={{width:'100%',height:100, backgroundColor:'red'}}>
 <Text>View1</Text>
</View>

//view2
<View style={{width:'100%',height:100, backgroundColor:'blue'}}>
  <Text>View2</Text>
</View>

我希望view1 默认显示并在我滚动到屏幕顶部时显示view2。将View 放在outputRange 中可以实现这一点吗?

【问题讨论】:

  • 你想在向下滚动一些动画时显示view2 来代替view1??
  • @AnkitMakwana 就像动画标题一样。我不想改变颜色,而是想滑动视图。
  • 滑动视图时,动画是什么?改变高度?还是不透明度?
  • @nikolai-serg 不太清楚你的意思。我想在滚动时更改视图。
  • 您的意思是您试图将view1 放入scrollView,并在滚动到特定位置时将view1 更改为view2

标签: javascript react-native


【解决方案1】:

如果你想动画改变视图,我想在 RN 中没有直接的方法,但是,在你的情况下,我可以想到一个使用 opacityposition: absoluteinterpolate() 混合的小技巧,在这里是一个最小的例子,你可以直接复制粘贴来测试它:

import React, { Component } from 'react';
import { StyleSheet, Animated, View, ScrollView } from 'react-native';

class AnimationExample extends Component {
  constructor(props) {
    super(props)
    this.state = {
      showBlueView: false,
      animatedOpacityValue: new Animated.Value(0),
    }
  }

  handleScroll = (event) => {
    const { animatedOpacityValue, showBlueView } = this.state;
    const scrollPosition = event.nativeEvent.contentOffset.y;

    if (scrollPosition > 100 && !showBlueView) {
      Animated.timing(animatedOpacityValue, {
        toValue: 1,
      }).start(() => this.setState({ showBlueView: true }))
    }

    if (scrollPosition < 100 && showBlueView) {
      Animated.timing(animatedOpacityValue, {
        toValue: 0,
      }).start(() => this.setState({ showBlueView: false }))
    }
  }

  render() {
    const { animatedOpacityValue } = this.state;
    return (
      <ScrollView
        style={styles.scrollView}
        onScroll={this.handleScroll}
        scrollEventThrottle={16}
      >
        <View style={styles.green} />
        <View style={styles.animatedViewsPositioner}>
          <Animated.View
            style={{
              ...styles.red,
              opacity: animatedOpacityValue.interpolate({
                inputRange: [0, 1],
                outputRange: [1, 0],
              }),
            }}
          />
          <Animated.View
            style={{
              ...styles.blue,
              opacity: animatedOpacityValue.interpolate({
                inputRange: [0, 1],
                outputRange: [0, 1],
              }),
            }}
          />
        </View>
      </ScrollView>
    )
  }
}

const styles = StyleSheet.create({
  scrollView: {
    flex: 1,
  },
  green: {
    height: 600,
    width: '100%',
    backgroundColor: 'green',
  },
  red: {
    height: 300,
    width: '100%',
    backgroundColor: 'red',
  },
  blue: {
    position: 'absolute',
    height: 300,
    width: '100%',
    backgroundColor: 'blue',
  },
  animatedViewsPositioner: {
    position: 'relative',
  },
})

在上面的示例中,我首先通过将handleScroll 函数应用于scrollView 来访问滚动位置。确保您将 scrollEventThrottle 设置为 16 以确保每秒触发该功能,但请注意可能导致的性能问题(如果您关心,您可以查看 this 以获取更多信息)。

为了实现当用户滚动到某个位置时触发的视图更改(实际上不是,但看起来像那样),我使用view 来包装红色和蓝色视图,红色的默认是@987654330 @,而默认为opacity: 0 的蓝色位于红色的顶部。

我通过使用interpolate()opacity 设置动画来隐藏红色视图并显示蓝色视图。借助它,两个不透明度值都由处于状态的一个动画值animatedOpacityValue 控制。我添加了一个状态showBlueView,通过避免不断设置由 onScroll 触发的状态来优化性能。


这是在两个视图上添加touchableOpacities 的更新,只需在未使用时隐藏蓝色视图即可。

首先,添加一个日志功能:

log = (stringToPrint) => () => {
  console.log(stringToPrint)
}

接下来,像这样更改scrollView,添加两个touchableOpacity

<ScrollView
  style={styles.scrollView}
  onScroll={this.handleScroll}
  scrollEventThrottle={16}
>
  <View style={styles.green} />
  <View style={styles.animatedViewsPositioner}>
    <Animated.View
      style={{
        ...styles.red,
        opacity: animatedOpacityValue.interpolate({
          inputRange: [0, 1],
          outputRange: [1, 0],
        }),
      }}
    >
      <TouchableOpacity
        style={{ backgroundColor: 'black', width: 80, height: 30 }}
        onPress={this.log('click on red')}
      />
    </Animated.View>
    {showBlueView && (
      <Animated.View
        style={{
          ...styles.blue,
          opacity: animatedOpacityValue.interpolate({
            inputRange: [0, 1],
            outputRange: [0, 1],
          }),
        }}
      >
        <TouchableOpacity
          style={{ backgroundColor: 'black', width: 80, height: 30 }}
          onPress={this.log('click on blue')}
        />
      </Animated.View>
    )}
  </View>
</ScrollView>

注意,我添加了showBlueView &amp;&amp;,当它的不透明度为0时隐藏蓝色视图,这样它就不会阻止任何应用于红色视图的点击事件(即使蓝色视图被隐藏,它实际上是在顶部opacity: 0 的红色视图)。

【讨论】:

  • 非常感谢您的回答。我正在尝试。我将TouchableOpacity 放在Animated.View 中,但不起作用。你知道如何处理Animated.View里​​面的TouchableOpacity吗?
  • 你的意思是不会工作?你想达到什么目标?
  • 对不起,如果我不清楚。我喜欢你的想法,而且效果很好。但我尝试滑动views 的原因是将buttonstouchableOpacity 放在animated.view 内。但是如果我在您的代码Animated.View 中放置buttontouchableOpacityonPress 将不起作用。
  • 这是因为您实际上是让蓝色视图覆盖了红色视图,您是否在红色视图上放置了touchableOpacity
  • 实际上我打算在两个视图中都有touchableOpacity。是否可以在两个视图中都有touchableOpacity
【解决方案2】:

@Andus 与 Animated.event 的回答

我们的想法是获取最新的 scrollY,然后将其包装为视图的不透明度。蓝色目标的示例输入范围是 0-50,不透明度为 1 到 0。这意味着它会在向下滚动前 50 像素时淡出。

红色的是相反的,输入范围为 0-200,不透明度为 0 到 1。(淡入)

import React, { Component } from 'react';
import { StyleSheet, Animated, View, ScrollView, SafeAreaView } from 'react-native';

export default class AnimationExample extends Component {
  constructor(props) {
    super(props)
    this.state = {
      scrollY: new Animated.Value(0)
    }
  }

  render() {
  const {scrollY} = this.state;
    return (
      <SafeAreaView style={{flex: 1}}>
      <ScrollView
        style={styles.scrollView}
        onScroll={Animated.event(
          [{nativeEvent: {contentOffset: {y: this.state.scrollY}}}]
        )}
        scrollEventThrottle={16}
      >
        <View style={styles.animatedViewsPositioner}>
          <Animated.View
            style={[styles.box, styles.target, {
             opacity: scrollY.interpolate({
                inputRange: [0, 50],
                outputRange: [1, 0],
              }),
            }]}
          />
          <Animated.View
           style={[styles.box, styles.origin, {
             opacity: scrollY.interpolate({
                inputRange: [0, 200],
                outputRange: [0, 1],
              }),
            }]}
          />
        </View>

      </ScrollView>
      </SafeAreaView>
    )
  }
}

const styles = StyleSheet.create({
  scrollView: {
    flex: 1,
  },
  box: {
    height: 1000,
    width: '100%',
    position: 'absolute'
  },
  origin: {
    backgroundColor: 'red',
    zIndex: 1
  },
  target: {
    backgroundColor: 'blue',
    zIndex: 2
  },
  animatedViewsPositioner: {
    position: 'relative',
    backgroundColor: 'pink',
    height: 10000
  },
})

【讨论】:

    【解决方案3】:

    如果您在显示View 时使用ScrollView,我相信您可以使用onScroll 回调来获取屏幕在ScrollView 内的位置,并在用户滚动时动态更改高度和颜色到顶部。

    <ScrollView onScroll={this.handleScroll} />
    

    并获得职位,

    handleScroll: function(event: Object) {
      console.log(event.nativeEvent.contentOffset.y);
    },
    

    参考:Get current scroll position of ScrollView in React Native

    【讨论】:

    • 感谢您的回复。我知道我可以动态改变颜色或高度。但这不是我想要达到的目标。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-25
    • 2018-07-24
    • 1970-01-01
    • 1970-01-01
    • 2018-03-12
    • 1970-01-01
    相关资源
    最近更新 更多