React Native - эффект отскока в iOS, использующий анимированный диффузор - PullRequest
0 голосов
/ 04 июля 2018

РЕДАКТИРОВАТЬ: Я ненавижу поиск ответов и нахожу некоторые вопросы, которые так и не были решены 10 лет назад, поэтому я отвечаю на свой вопрос для тех, кто может захотеть узнать. В моем случае я просто отключил опору bounces для прокрутки. Поскольку FlatList расширяет ScrollView в React, установка bounces на false в созданном мною анимированном компоненте FlatList не дает ему отскочить и решает мою проблему. Хорошего дня.

надеюсь, у тебя будет отличный день. Я пытаюсь динамически анимировать свой заголовок, но по какой-то причине, когда я прокручиваю за пределы начала или конца прокрутки, эффект отскока смешивается с анимацией (как показано на рисунке ниже)

GIF

Тот же GIF, но с более высоким разрешением

Как вы можете видеть, когда я прокручиваю наверх и включаю анимацию отказов, заголовок думает, что я прокручиваю вниз, поскольку отскок возвращает первый элемент в списке обратно наверх. Как это исправить? Я где-то видел в Интернете, что добавление интерполятора к анимированному значению поможет, хотя я не совсем понимаю. Ниже мой код. Спасибо

const AnimatedFlatList = Animated.createAnimatedComponent(FlatList)

const tempArray = [
  ...(an array of my data)
]

export default class TempScreen extends React.Component {
  static navigationOptions = {
    header: null
  }

  constructor(props) {
    super(props)
    this.state = {
      animatedHeaderValue: new Animated.Value(0),
    }
  }

  render() {
    const animatedHeaderHeight = Animated.diffClamp(this.state.animatedHeaderValue, 0, 60)
      .interpolate({
        inputRange: [0, 70],
        outputRange: [70, 0],
      })
    return ( <
      View >
      <
      Animated.View style = {
        {
          backgroundColor: 'white',
          borderBottomColor: '#DEDEDE',
          borderBottomWidth: 1,
          padding: 15,
          width: Dimensions.get('window').width,
          height: animatedHeaderHeight,
        }
      } >
      <
      /Animated.View> <
      AnimatedFlatList scrollEventThrottle = {
        16
      }
      onScroll = {
        Animated.event(
          [{
            nativeEvent: {
              contentOffset: {
                y: this.state.animatedHeaderValue
              }
            }
          }]
        )
      }
      data = {
        tempArray
      }
      renderItem = {
        ({
          item
        }) =>
        <
        View style = {
          {
            flex: 1
          }
        } >
        <
        Text style = {
          {
            fontWeight: 'bold',
            fontSize: 30
          }
        } > {
          item.name
        } < /Text> <
        Text > {
          item.year
        } < /Text> <
        /View>
      }
      />

      <
      /View>

    )
  }
}

Ответы [ 2 ]

0 голосов
/ 04 января 2019

У меня была та же проблема, что и два часа назад ...

Вы можете установить Scrollview свойство bounces=false, но если вы хотите RefreshControl для обновления контента ScrollView (как в моем случае), свойство bounce должно оставаться активным.

Я исправил это после этой классной статьи: https://medium.com/appandflow/react-native-collapsible-navbar-e51a049b560a.

Я не эксперт по анимированной библиотеке, поэтому выкладываю свой код:

constructor(props) {

    const scrollAnim = new Animated.Value(0);
    const offsetAnim = new Animated.Value(0);

    this.state = {
        scrollAnim,
        offsetAnim,
        AnimatedViewHeight: 1,
        clampedScroll: Animated.diffClamp(
            Animated.add(
                scrollAnim.interpolate({
                    inputRange: [0, 1],
                    outputRange: [0, 1],
                    extrapolateLeft: 'clamp',
                }),
                offsetAnim
            ),0, 1
        )
    }
}

render() {

    const minScroll = this.state.AnimatedViewHeight;

    const navbarTranslate = this.state.clampedScroll.interpolate({
        inputRange: [0, minScroll],
        outputRange: [0, -minScroll],
        extrapolate: 'clamp',
    });

    return (
        <View style={{
            flex: 1
        }}>

      <Animated.View
        onLayout={(event) => {
          var { height } = event.nativeEvent.layout;
          this.setState({
              AnimatedViewHeight: height,
              clampedScroll: Animated.diffClamp(
                    Animated.add(
                          this.state.scrollAnim.interpolate({
                                inputRange: [0, 1],
                                outputRange: [0, 1],
                                extrapolateLeft: 'clamp',
                          }),
                          this.state.offsetAnim
                    ), 0, height)
          })
        }}
        style={[{ transform: [{ translateY: navbarTranslate }] }]}>

       <View><Text>THIS IS YOUR HEADER</Text></View>

       </Animated.View>

       <AnimatedFlatList
           // iOS offset for RefreshControl
           contentInset={{
               top: this.state.AnimatedViewHeight,
           }}
           contentOffset={{
               y: -this.state.AnimatedViewHeight,
           }}
           scrollEventThrottle={1}
           onScroll={
               Animated.event(
                        [{ nativeEvent: { contentOffset: { y: this.state.scrollAnim } } }],
                        { useNativeDriver: true },
               )}
               data={this.state.data}
               keyExtractor={(item, idx) => idx}
               ListFooterComponent={this.renderFooter}
               renderItem={this.renderItem}
               onEndReached={this.handleLoadMore}
               refreshControl={
                    <RefreshControl
                        refreshing={this.state.refreshing}
                        onRefresh={this.onRefresh}
                        // Android offset for RefreshControl
                        progressViewOffset={this.state.AnimatedViewHeight}
                    />
                }
                onEndReachedThreshold={0.5} />
        </View>

    )
}

this.state.AnimatedViewHeight - высота заголовка, полученная путем вызова функции onLayout. Внутри этой функции я также установил новый fixededScroll, потому что у меня новая высота (в моем случае заголовок не имеет фиксированного размера). После этого в render() определите переменную (navbarTranslate) для управления headerSize на основе позиции прокрутки вашего анимированного прокрутки.

0 голосов
/ 01 августа 2018

Если вы хотите решить только проблему «отказов», проблема в том, что iOS дает diffClamp отрицательные значения scrollY. Вам необходимо отфильтровать их и убедиться, что scrollY остается> = 0, чтобы избежать изменения прокрутки в diffClamp.

const clampedScrollY = scrollY.interpolate({
  inputRange: [0, 1],
  outputRange: [0, 1],
  extrapolateLeft: 'clamp',
});

Еще один приятный трюк - использовать технику «скалы», чтобы заголовок исчезал только после минимальной позиции scrollY.

Вот код из моего приложения:

const minScroll = 100;

const clampedScrollY = scrollY.interpolate({
  inputRange: [minScroll, minScroll + 1],
  outputRange: [0, 1],
  extrapolateLeft: 'clamp',
});

const minusScrollY = Animated.multiply(clampedScrollY, -1);

const translateY = Animated.diffClamp(
  minusScrollY,
  -AnimatedHeaderHeight,
  0,
);

const opacity = translateY.interpolate({
  inputRange: [-AnimatedHeaderHeight, 0],
  outputRange: [0.4, 1],
  extrapolate: 'clamp',
});

clampedScrollY будет:

  • 0, когда scrollY = 0
  • 0, когда scrollY = 50
  • 0, когда scrollY = 100
  • 30 при scrollY = 130
  • 170, когда scrollY = 270

Вы поняли идею. Так что diffClamp будет> 0, только если scrollY> 100, и увеличит 1 на 1 после этого порога.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...