当你不知道内容的大小时,你如何设置反应原生的高度动画?

时间:2017-08-11 11:12:29

标签: animation react-native

在本地反应中,当你不知道它的内容大小时,如何为视图的大小设置动画?

假设View的内容高度可以是0-400pts,内容可以动态增长和缩小,并且您想要对高度的任何更改进行动画处理。

基本上,我希望在不使用LayoutAnimation 的情况下重现LayoutAnimation 的行为,但使用Animated。

我认为我不知道的是我不知道如何为目标高度设置动画,因为我不知道内容的高度。

4 个答案:

答案 0 :(得分:10)

我使用LayoutAnimation,就在导致组件高度发生变化的状态更改之前,添加:

LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);

您可以使用不同的预设:

答案 1 :(得分:8)

您将不得不添加某种尺寸缩放,可能是百分比以获得最佳效果。

首先,您需要使用Animated.View而不是View

接下来,您需要对视图的样式应用变换,让它看起来如下所示。这是更新和更改并创建动作的部分。

        <Animated.View style={[ styles.someStyleYouMade,
          {
            transform: [
              // scaleX, scaleY, scale, theres plenty more options you can find online for this.
              {  scaleX: ViewScaleValue } // this would be the result of the animation code below and is just a number.
            ]
          } 
        ]}
        >

下一部分基本上是一个动画API示例,您可以编写类似这样的内容(根据需要自定义),并且在调用此脚本时,它将以您指定的任何方式进行动画处理。

  Animated.timing(                    // Animate over time
    this.state.ViewScale,             // The animated value to drive, this would be a new Animated.Value(0) object.
    {
      toValue: 100,                   // Animate the value
      duration: 5000,                 // Make it take a while
    }
  ).start();

最后,您可能希望对值应用插值,使其看起来尽可能自定义。

(这将进入render()功能但在return()之前。ViewScaleValue将进入Animated.View转换

const ViewScaleValue = this.state.ViewScale.interpolate({
  inputRange: [0, 25, 50, 75, 100],
  outputRange: [0, .5, 0.75, 0.9, 1]
});

所有这些代码都会使ViewScaleValue,一个简单的数字,从0到100的动画,快速然后慢(因为插值)并将动画的每次迭代应用到Animated.View变换

与此同时阅读Animated API以便更好地掌握它。

答案 2 :(得分:1)

我采用的方法是花费布局遍历来获取“截断的”组件的高度和“全尺寸的”组件的高度(通常需要知道如何确定截断的高度,这需要一种方法)呈现内容的“行”)。本质上,在拥有这些值之前,请将它们呈现为两个单独的隐藏视图:

hidden: {
  position: 'absolute',
  left: 0,
  top: 0,
  opacity: 0,
},

使用onLayout捕获其高度:

const onLayoutTruncated = ({nativeEvent}: LayoutChangeEvent) => {
  if (!doneProcessing) {
    truncatedHeight = nativeEvent.layout.height;
    checkIfDoneProcessingLayout();
  }
};

const onLayoutFull = ({nativeEvent}: LayoutChangeEvent) => {
  if (!doneProcessing) {
    fullHeight = nativeEvent.layout.height;
    checkIfDoneProcessingLayout();
  }
};

checkIfDoneProcessingLayout()将检查是否同时设置了truncatedHeightfullHeight,如果两者都设置了,则进行状态更改(doneProcessing = true)。

您应该从那里取消隐藏截断的视图,并能够使用Animated.Value和插值在两个高度值之间建立动画:

const expandingHeight = animatedValue.interpolate({
  inputRange: [0, 1],
  outputRange: [truncatedHeight, fullHeight],
});

使用Animated.timing触发点击时展开/折叠的动画

Animated.timing(animatedValue, {toValue: isExpanded ? 0 : 1, easing: SOME_EASING_FUNCTION, duration: SOME_DURATION}).start();

答案 3 :(得分:1)

大家好,希望还不晚...

适用于任何使用View高度处理React Native动画的人。
我知道这很烦人:

✖️React Native动画似乎不支持布局样式(例如,宽度和高度)
✖️LayoutAnimation看起来很难调查
W️希望使用一种官方方式制作动画,而不是安装第三方程序包
✖️有时内容可能很大,破坏了您的视图样式

这是我为您提供的解决方案(类组件方式):

首先,将动画值设置为状态:

state = { height: new Animated.Value(0) };

接下来,通过动画插值设置动画视图的max height

const maxHeight = this.state.height.interpolate({ 
  inputRange: [0, 1], 
  outputRange: [0, 2000]  // <-- any value larger than your content's height
};
return (<Animated.View style={[styles.box, { maxHeight: maxHeight }]} />); 
// any other fixed styles in styles.box

然后,将动画设置在您调用的function内,
componentDidMount(如果希望它在呈现后立即显示):

// or in any function that users interact
componentDidMount() {
  Animated.timing(this.state.formHeight, {
    toValue: 1,
    duration: 500,           // <-- animation duration
    easing: Easing.linear,   // <-- or any easing function
    useNativeDriver: false   // <-- need to set false to prevent yellow box warning
  }).start();
}

请注意,因为布局样式不支持将useNativeDriver设置为true


样本

因此,下面是供您与之交互的示例,
随时复制并粘贴到您的React Native项目以进行尝试:

import React, { PureComponent } from 'react';
import { Animated, Button, Easing, View, Text, StyleSheet } from 'react-native';

class AnimateBox extends PureComponent {
  state = { opacity: new Animated.Value(0), height: new Animated.Value(0) };

  showContent = () => {
    const { opacity, height } = this.state;

    Animated.timing(height, {
      toValue: 1,
      duration: 500,
      easing: Easing.linear,
      useNativeDriver: false  // <-- neccessary
    }).start(() => {
      Animated.timing(opacity, {
        toValue: 1,
        duration: 500,
        easing: Easing.linear,
        useNativeDriver: false  // <-- neccessary
      }).start();
    });
  };

  render() {
    const { opacity, height } = this.state;
    const maxHeight = height.interpolate({ 
      inputRange: [0, 1], 
      outputRange: [0, 1000]  // <-- value that larger than your content's height
    });

    return (
      <View style={styles.box}>
        <Animated.View style={{ opacity: opacity, maxHeight: maxHeight }}>
          <Text style={styles.content}>
            Lorem Ipsum is simply a dummy text of the printing and typesetting industry.
            Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
            when an unknown printer took a galley of type and scrambled it to make a type specimen book.
            It has survived not only five centuries, but also the leap into electronic typesetting,
            remaining essentially unchanged. It was popularised in the 1960s with the release of
            Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
          </Text>
        </Animated.View>
        <View style={styles.spacing}>
          <Button title="Show content" onPress={this.showContent} />
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  box: {
    backgroundColor: '#fff',
    marginHorizontal: 15,
    paddingHorizontal: 15
  },
  spacing: {
    paddingVertical: 10
  },
  content: {
    fontSize: 16,
    lineHeight: 30,
    color: '#555'
  }
});


export default AnimateBox;

快乐编码:)