基于多个ScrollView的动画单个视图的实现方法

30

我正在开发一个应用程序,尝试基于多个ScrollView的滚动位置来实现一个View的动画。

这是屏幕的外观。

Profile Screen

上面的屏幕有两部分

  • 顶部的View组件
  • 底部的TabNavigator组件

TabNavigator中的每个选项卡都有一个ScrollView(在本例中有2个,但可以更多)。 我想实现的是当用户向下滚动时折叠View,并在用户向上滚动时展开它。 单个选项卡上做得很好,它正是我想要的,但当我添加第二个选项卡时出现了问题。

问题

当我在选项卡1上稍微滚动一点,并转到选项卡2并尝试滚动时,它会变得卡顿。 看看GIF以理解我的意思

React Native Animation Jerk


更新

在expo.io上检查这个snack以实时查看问题

snack.expo.io/SytBkdBAW


我尝试的事情

App.js

export default class App extends Component {

  constructor (props) {
    super(props)

    this.state = {
      /* omitted - not related */
      scrollY: new Animated.Value(0)
    }
  }

  render () {

    let translateY = this.state.scrollY.interpolate({
      inputRange: [0, 600],
      outputRange: [0, -290],
      extrapolate: 'clamp'
    });

    let TabsTranslateY = this.state.scrollY.interpolate({
      inputRange: [0, 600],
      outputRange: [0, -290],
      extrapolate: 'clamp'
    });

    return (
      <View style={styles.container}>
        <Animated.View style={{transform: [{translateY: translateY}], overflow: 'hidden'}}>
          <Text style={styles.welcome}>
            Welcome to React Native!!
          </Text>

          <Text style={styles.time}>
            {this.state.hour} : {this.state.minute}
          </Text>

          <TouchableOpacity onPress={() => { /* omitted */ }} style={styles.button}><Text style={styles.buttonText}>Set Time</Text></TouchableOpacity>
        </Animated.View>
        <Animated.View style={{
          flex: 0,
          transform: [{translateY: TabsTranslateY}],
          height: Dimensions.get('window').height
        }}>
          <Tabs removeClippedSubviews={false} screenProps={{animatedScrollY: this.state.scrollY}}/>
        </Animated.View>
      </View>
    )
  }
}

const styles = StyleSheet.create({/* omitted styles*/})

主页.js (选项卡1)

/* omitted imports */
export default class Home extends Component {
  /* omitted navigation options */
  constructor (props) {
    super(props)

    this.state = {
      scrollY: this.props.screenProps.animatedScrollY
    }

  }

  render () {
  return (
      <View>
        <Animated.ScrollView onScroll={Animated.event(
          [{nativeEvent: {contentOffset: {y: this.state.scrollY}}}],
          {useNativeDriver: true}
        )} scrollEventThrottle={16}>

          {Array(90).fill().map((v, i) => {
            return <Text key={i}
                         style={{flex: 1, backgroundColor: '#333', padding: 20, marginVertical: 10, color: 'white'}}>Item
              #{i + 1}</Text>
          })}
        </Animated.ScrollView>
      </View>
    )
  }
}

照片.js(选项卡2)

/* omitted imports */
export default class Photos extends Component {
  /* omitted navigation options */
  constructor (props) {
    super(props)

    this.state = {
      PhotosScrollY: this.props.screenProps.animatedScrollY
    }
  }

  render () {
    return (
      <Animated.ScrollView onScroll={Animated.event(
        [{nativeEvent: {contentOffset: {y: this.state.PhotosScrollY}}}],
        {useNativeDriver: true}
      )} scrollEventThrottle={16}>

        <View style={{flex: 1,}}>
          {Array(90).fill().map((v, i) => {
            return <View key={i} style={/* omitted */}>
              <Text style={/* omitted */}>
                Photo #{i + 1}
              </Text>
            </View>
          })}
        </View>

      </Animated.ScrollView>
    )
  }
}

我不确定如何克服这个问题,欢迎提出任何建议和解决方案。

谢谢。


如果您直接从props中使用它而不将其分配给state会怎样? - Alexander Vitanov
@AlexanderVitanov,你是在说animatedScrollY吗?- 不,我不能直接从状态中在选项卡中使用它,因为它只在App.js中可用,即使我这样做了,我认为也不会改变什么。 - Azeem Hassni
当然可以在 snack https://snack.expo.io/SytBkdBAW 中检查它 -> /Tabs/index.js - Azeem Hassni
假设您打开应用程序,在主页选项卡上向下滚动一点,然后切换到项目选项卡。项目列表应该在顶部吗?如果是这样,并且没有地方可以向上滚动,您将如何显示标题? - Rob Hogan
如果你正在寻找像这个 https://www.npmjs.com/package/react-native-collapsible-tab-view 这样的效果,那么这是一个非常棒的库。 - Azeem Hassni
显示剩余2条评论
3个回答

5

尝试使用 Animated.add()

所以你需要在App中使用它。

const tab1ScrollY = new Animated.Value(0)
const tab2ScrollY = new Animated.Value(0)
const scrollY = Animated.add(tab1ScrollY,tab2ScrollY)

scrollYtab1滚动条tab2滚动条 的偏移量之和。


谢谢,这几乎解决了我的问题,除了一个小问题,在Tab1上滚动后,当您导航到Tab2时,您无法向下滚动,因此标题会被卡住,除非您返回Tab1并滚动。您可以在此处检查此行为https://snack.expo.io/rJXwrLQ1G - Azeem Hassni
如果你有解决我在评论中指定的问题的方案,我会接受这个答案并奖励赏金点数。 - Azeem Hassni
@AzeemHassni,你解决了你的问题(标题卡住)吗? - Tejas Khutale

3

我遇到了这个问题。我尝试用这种方式解决它。(仍在优化中...)

<ParentComponent>
    <CollapsibleHeader/>
    <TabNavigator/>
</ParentComponent>

滚动状态位于父组件中,TabNavigator内部的选项卡具有滚动视图。
我使用回调函数在绑定动画事件后更新父组件中的状态。
因此,每当您在两个选项卡之间移动时,状态位于父组件中,并且从不同的地方进行更新。
也许这可以帮助您。
注意:仍在优化中。
---------编辑---------
父组件:
状态:scrollY: new Animated.Value(0)
componentDidMount(){
    binding event.
}
componentWillUnmount(){
    Unbind event.
}
onScrollEventCaptured(){
  (update parent state and send it to <CollapsibleHeader/>)*
}

选项卡1:

这里有本地状态。(优化此部分)

scrollY: new Animated.Value(0)

有一个ListView(列表视图)

在ListView上有一个onScroll函数:

onScroll={Animated.event([{
                        nativeEvent: {
                            contentOffset: {
                                y: this.state.scrollY
                            }
                        }
                    }], {
                        listener: (event) => {
                            AppEvents.fire("topBar", this.state.scrollY);
                        },
                    })}

AppEvents.fire() 是在父组件中被捕获的事件。(然后捕获的事件设置了状态,并作为属性传递给实际执行动画的组件)。*

对于标签页2: 与标签页1相同。

*两个都是一样的。

仍在对此进行优化。对我来说,在iOS开发中,动画有点卡顿,但在生产iOS应用程序中看起来很好。Android无处不在,卡顿。


如果我理解正确的话,这正是我正在做的事情。能否分享你的代码,以便我可以比较一下,找出我所缺少的部分。 - Azeem Hassni
修改了我的回答,您可以检查一下它是否有帮助。 - Ashwin Mothilal
你在使用这个方法时遇到了任何问题吗?请告诉我,因为我们也在不断改进它。 - Ashwin Mothilal
我在实现这个解决方案时遇到了问题。你能否更新一下这个小吃(https://snack.expo.io/SytBkdBAW),这样我就可以阅读你的代码并实现它。 - Azeem Hassni

2

我从未使用过react-native,但我可以确定你遇到了什么问题。我使用调试工具,发现每次点击选项卡时,应用程序都会调用以下代码:

let translateY = this.state.scrollY.interpolate({
      inputRange: [0, 600],
      outputRange: [0, -290],
      extrapolate: 'clamp'
    });

    let TabsTranslateY = this.state.scrollY.interpolate({
      inputRange: [0, 600],
      outputRange: [0, -290],
      extrapolate: 'clamp'
    });

这里涉及到 IT 技术,需要翻译的内容是:

在整个过程中,this.state.scrollY = new Animated.Value(0) 始终存在。

基本上,这意味着当您点击选项卡并开始滚动时,它将从 0 开始滚动。您需要找到一个解决方案来记住 Animated.Value 的先前状态或更改动画的输入/输出范围。

以下是如何从 App.js 中获取选项卡点击事件的示例:

<Tabs removeClippedSubviews={false} screenProps={{animatedScrollY: this.state.scrollY}}
          onNavigationStateChange={(prevState, currentState,action) => {
            console.log(currentState);
          }}
 />

希望这能为您提供帮助。


“where this.state.scrollY = new Animated.Value(0) all the time.” 不是初始值。当您开始滚动时,此值会得到更新。 - Azeem Hassni
如果您能在这个小吃 https://snack.expo.io/SytBkdBAW 中实现您的想法并分享更新后的链接,那就太好了。谢谢。 - Azeem Hassni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接