5

This is a video of what I have so far https://drive.google.com/file/d/1tJMZfpe8hcqLcNMYiE-6pzwHX0eLofv1/view?usp=sharing

I'm trying to make the "Available for 3 months" fixed once the contentOffset.y has reached the header. Similar to what a position: sticky in css would do.

So far, I thought of doing this via onScroll prop in the Scroll View Component but the issue is, I already have animations (Animated.Event) from the parent and child component in place, meaning the only way for me to do this would be via the listener function in the Animated.Event but doing that, leads to a super choppy animation if the useNativeDriver option is set to false.

If I set that to true, the whole thing won't work (it crashes). The error is along the lines "onScroll is not a function, it's an instance of Animated.event"

So, Say we have two components, the parent is Parent.js and the child (a scroll view) is ChildScrollView.js

The ChildScrollView.js already has animations on the scroll view, but we need to add some more in the Parent.js component, to do with Navigation which the ChildScrollView.js can't handle

So it's coded like this:

Parent.js

componentWillMount() {
    const { navigation } = this.props
    const { scrollY } = this.state

    const bgColor = scrollY.interpolate({
      inputRange: [HEADER_HEIGHT / 4, HEADER_HEIGHT / 2],
      outputRange: ['transparent', 'rgb(255,255,255)'],
      extrapolate: 'clamp',
    })

    const titleColor = scrollY.interpolate({
      inputRange: [0, HEADER_HEIGHT / 2],
      outputRange: ['transparent', colors.paragraphs],
      extrapolate: 'clamp',
    })

    const titleMarginTop = scrollY.interpolate({
      inputRange: [0, HEADER_HEIGHT / 2],
      outputRange: [HEADER_HEIGHT, 0],
      extrapolate: 'clamp',
    })

    navigation.setParams({
      bgColor,
      titleColor,
      titleMarginTop,
    })
}

onScroll() {
}

render() {

  return (
    <ChildScrollView
      {...childProps}
      onScroll={Animated.event([
        { nativeEvent: { contentOffset: { y: scrollY } } },
      ], {
        useNativeDriver: false, // when I do this, it become super laggy, but setting it to true makes the app crash
        listener: this.onScroll,
      })}
    >
      <MakeMeFixedOnScroll>I could do this in css with "position: sticky"</MakeMeFixedOnScroll>
    </ChildScrollView>
  )
}

And the child is similar,

<Animated.ScrollView
 {...props}
 onScroll={Animated.event(
   [{ nativeEvent: { contentOffset: { y: scrollY } } }],
            {
              useNativeDriver: false,
              listener: event => {
                if (onScroll) onScroll(event)
              },
            }
          )}
          scrollEventThrottle={16}
        >
Jesse Onolemen
  • 1,277
  • 1
  • 15
  • 32
  • This [article](https://medium.com/appandflow/react-native-scrollview-animated-header-10a18cb9469e) might offer some help :) – VilleKoo Apr 18 '19 at 10:43
  • @VilleKoo Thanks for that, I've already done what was set up in that article pretty much. The issue is I need to add an animation that does not rely on the Animated.Value() but on the contentOffset. The only way to do this is by adding a "listener" function, but when I do that - enabling the "useNativeDriver" leads to an error [onScroll is not a function, it's an instance of Animated.event]. Without it, the animation is way too laggy – Jesse Onolemen Apr 18 '19 at 10:53
  • 1
    Hi @StephenJesse , I've encountered the same situation. Do you have any updates or solution on this?I'd be very happy if you can help me – Ken Pham May 07 '20 at 10:44
  • 1
    @marwin sorry I haven't been active on this site for a while, what I ended up doing was just adding a listener to the scroll Y value. Once it reached a certain position, which I calculated by adding the frame heights of the image, title etc, I set the position of the banner to fixed, and the top, left, right insets to 0. Did the same thing in reverse scroll. Probably not a good solution, I'd imagine now you could something a lot cleaner possibly using React native re-animated but I ended up abandoning that project and I haven't gone back to it since. I can send you the code if you want? – Jesse Onolemen Jun 12 '20 at 17:59
  • @StephenJesse Thanks so much for the response Stephen. I've tried to use that same methodology, but in the end, it;s not as smooth as I expected. Yes, thank you so much. I'd love to have some look at your code – Ken Pham Jun 14 '20 at 04:52

1 Answers1

0

I would use SectionList

<SectionList
  renderItem={({item, index, section}) => (
    <Text style={styles[item.type]}>{item.text}</Text>
  )}
  renderSectionHeader={({ section: { title } }) => (
    title && <Text style={styles.sticky}>{title}</Text>
  )}
  sections={[
   { title: null, data: [{
       type: 'heading', text: '133 Random Road'
     }, {
       type: 'heading', text: 'Donnybrook'
     }, {
       type: 'subtitle', text: 'Dublin 4'
     }, {
       type: 'title', text: 'From E1000/month'
     }]
   },
   { title: 'Available For 3 Month', data: [{
       type: 'text', text: 'Beautiful for bedroom...'
     }]
   }
  ]}
  stickySectionHeadersEnabled
/>
Medet Tleukabiluly
  • 11,662
  • 3
  • 34
  • 69
  • I actually managed to accomplish it by adding a listener to the "Animated.Value". I need to do some testing, however, to make sure that it works on all devices. Tested on my iPhone XS Max. – Jesse Onolemen Apr 18 '19 at 11:48
  • 1
    You sure it will probably work on android? Not Over-engineering is important – Medet Tleukabiluly Apr 18 '19 at 11:51
  • That's a fair point. I don't know how well a section list would work with the whole Parralax Header that I have set up already – Jesse Onolemen Apr 18 '19 at 12:04