简体   繁体   English

react-native :根据滚动位置更改视图

[英]react-native : change view corresponding to scroll position

I am using Animation.view to change the height and the background of the header .我正在使用Animation.view来更改headerheightbackground

I set my height and the background settings like this:我设置我的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'
    })

This is my animated.view.这是我的动画视图。

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

Everything works well.一切正常。

My question is there a way I could change the view like the height and the backgroundcolor ?我的问题是有没有一种方法可以更改视图,例如heightbackgroundcolor

For example, say I have two views:例如,假设我有两种观点:

//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>

I want the view1 to show by default and show view2 as I scroll to the top of the screen.我希望view1默认显示并在我滚动到屏幕顶部时显示view2 Placing the View in the outputRange would make this possible?View放在outputRange会使这成为可能吗?

I guess there's no direct way in RN if you want to animated a change of view, however, in your case I can think of a little trick using the mix of opacity , position: absolute and interpolate() , here is a minimal example which you can directly copy and paste to test it:我想如果你想改变视图,在 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',
  },
})

In the example above, I first access the scroll position by applying a handleScroll function to the scrollView .在上面的例子中,我第一接入通过施加滚动位置handleScroll功能的scrollView Make sure you have scrollEventThrottle set to 16 to ensure the function is triggered every second, but beware of possible performance issue caused by that (if you care, you might take a look at this for more info).确保将scrollEventThrottle设置为 16 以确保每秒触发该功能,但要注意由此可能导致的性能问题(如果您关心,可以查看了解更多信息)。

To achieve a view change triggered when user scroll to certain position (which is actually not, but it looks like that), I use a view to wrap both red and blue views, the red one is default with opacity: 1 , while the blue one with default opacity: 0 , sitting on top of the red one.为了实现当用户滚动到某个位置时触发的视图更改(实际上不是,但看起来像那样),我使用一个view来包装红色和蓝色视图,红色的默认为opacity: 1 ,而蓝色的默认opacity: 0的一个,位于红色的顶部。

I hide the red view and show the blue one by animating their opacity using interpolate() .我通过使用interpolate()它们的opacity动画来隐藏红色视图并显示蓝色视图。 With the help of that, both opacity values are controlled by one animatedValue animatedOpacityValue put in the state.在此帮助下,两个不透明度值都由一个放在状态中的 animationValue animatedOpacityValue控制。 I added a state showBlueView to optimise the performance by avoid constantly setting states triggered by onScroll.我添加了一个状态showBlueView通过避免不断设置由 onScroll 触发的状态来优化性能。


Here's an update to add touchableOpacities on both views, simply achieve by hiding the blue view when it's unused.这是在两个视图上添加touchableOpacities的更新,只需在未使用时隐藏蓝色视图即可实现。

First, add a log function:首先,添加一个日志功能:

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

Next, change the scrollView like this by adding two touchableOpacity其次,改变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>

Note that I added showBlueView && to hide the blue view when its opacity is 0, so that it will not block any click event applied to the red view (even though the blue view is hidden, it is actually on top of the red view with opacity: 0 ).请注意,我添加了showBlueView &&以在不透明度为 0 时隐藏蓝色视图,以便它不会阻止应用于红色视图的任何点击事件(即使蓝色视图被隐藏,它实际上位于红色视图之上opacity: 0 )。

@Andus 's ans with Animated.event @Andus 对Animated.event回答

The idea is to get the latest scrollY then wrap it to view's opacity.我们的想法是获取最新的 scrollY,然后将其包装为视图的不透明度。 The example input range of blue target is 0-50 and got opacity 1 to 0. That means it would fade out when scrolling down the first 50 px.蓝色目标的示例输入范围为 0-50,不透明度为 1 到 0。这意味着它会在向下滚动前 50 像素时淡出。

The red one is the reverse one with input range 0-200 and out to opacity 0 to 1.(fade in)红色的是反向的,输入范围为 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
  },
})

If you are using ScrollView in displaying the View , I believe you can use the onScroll callback to get the position of your screen inside the ScrollView and change the height and color dynamically when your user scroll to the top.如果您在显示View使用ScrollView ,我相信您可以使用onScroll回调来获取屏幕在ScrollView的位置,并在用户滚动到顶部时动态更改高度和颜色。

<ScrollView onScroll={this.handleScroll} />

And getting the position,并获得职位,

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

Reference: Get current scroll position of ScrollView in React Native参考: 在 React Native 中获取 ScrollView 的当前滚动位置

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM