当您不知道内容的大小时,如何在 react native 中为高度设置动画?

Posted

技术标签:

【中文标题】当您不知道内容的大小时,如何在 react native 中为高度设置动画?【英文标题】:How do you animate the height in react native when you don't know the size of the content? 【发布时间】:2018-01-19 21:21:20 【问题描述】:

在 react-native 中,如果不知道 View 内容的大小,如何为 View 的大小设置动画?

假设 View 的内容高度可以在 0-400pts 之间,内容可以动态增长和缩小,并且您希望对高度的任何变化进行动画处理。

基本上,我希望重现 LayoutAnimation 的行为不使用 LayoutAnimation,而是使用 Animated。

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

【问题讨论】:

您使用翻译值制作动画,请查看***.com/questions/55339044/… 【参考方案1】:

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

首先您需要使用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 以更好地掌握它。

【讨论】:

使用这种方法,如何在不缩放的情况下调整高度?似乎只有比例选项可用。 当动画视图的值为零时,它就是不可见的。不过,它正在占据内容可见时所需的位置 为什么是%?你能解释一下百分比将如何帮助 OP 实现他们的目标吗?【参考方案2】:

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

LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);

您可以使用不同的预设:

弹簧 easeInEaseOut

线性

您可以在此处阅读更多信息https://facebook.github.io/react-native/docs/layoutanimation.html

【讨论】:

LayoutAnimation 将为所有内容设置全局动画,此方法仅适用于您有一个非常简单的应用程序,其中一次发生 1 个操作。 LayoutAnimation 在 iosandroid 上仍然存在许多致命问题。如果您正在考虑支持各种移动设备,我一直建议使用 Animated API。 LayoutAnimation 在 Android 上存在很多问题,导致我的应用在生产中出现各种崩溃。我不得不替换它(即Platform.OS === 'ios' ? LayoutAnimation.configureNext(LayoutAnimation.create(50, 'easeInEaseOut', 'opacity')) : null;)并将其替换为仅 iOS 调用。 我在下面为任何在处理动画时遇到类似情况的人做出了回答。它可以处理内容的高度使用Animated组件。随意看看;D干杯!【参考方案3】:

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

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();

【讨论】:

切换时无需触摸状态。使用,toValue: !animatedValue.__getValue()【参考方案4】:

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

适用于在视图高度上处理 React Native 动画的任何人。 我知道这很烦人:

✖️React Native动画似乎不支持布局样式(例如宽度和高度) ✖️ LayoutAnimation 看起来很难调查 ✖️ 希望用官方的方式制作动画而不是安装第三方包 ✖️ 有时内容可能会影响您的视图样式

所以这是我给你的解决方案(类组件方式):

首先,在状态中设置动画值:

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;

快乐编码:)


更多解释(2021 年 2 月 3 日编辑)

如 React Native 文档中所述:https://reactnative.dev/docs/animations#caveats

原生驱动程序目前并不支持您可以使用 Animated 执行的所有操作。主要限制是您只能为非布局属性设置动画:像 transform 和 opacity 这样的东西会起作用,但 Flexbox 和 position 属性不会。

禁用useNativeDriver可以为opacitytransform以外的样式设置动画,但实际上增加了JS线程的工作量,因为JS线程需要计算每一帧的UI。 p>

在 React Native 0.62 的博客中:https://reactnative.dev/blog/2020/03/26/version-0.62#deprecations

现在需要设置 useNativeDriver 以支持将来切换默认。

您必须在React native v0.62或以上的动画选项中设置useNativeDriver以防止出现任何警告。

【讨论】:

真的吗?为什么它对我说“原生动画模块不支持样式属性'高度'”。想哭了 嗨@CharithaGoonewardena,我认为您可能已将useNativeDriver 设置为true,这会导致该警告。我在答案中添加了一些解释。随意查看,或将示例代码复制到您的项目中进行编辑。干杯! 这行得通,但你真的不需要插值。您可以将动画设置为 2000 而不是 1 并将 maxHeight 设置为 this.state.formHeight 是的,当然@Johnkendall :) 在我的示例中,插值仅用于处理具有相同动画值的 maxHeight 和 opacity。如果您没有其他动画样式,则由您定义它的最大值。 在android中动画根本不是过渡。它只是直接价值。有人遇到过这个吗? IOS 工作正常

以上是关于当您不知道内容的大小时,如何在 react native 中为高度设置动画?的主要内容,如果未能解决你的问题,请参考以下文章

当您不知道页数时,如何使用 Node.js 在 while 循环中向 API 发出多个分页 GET 请求?

当您不知道键的名称时访问对象内部的数据

使用基类指针查找派生类对象的大小

Angular 2:当您不知道 childComponent 的类时,如何将 childComponent 属性发送给 parent?

当您不记得约束名称时如何删除约束[重复]

React:如何在内容大小未知时为展开和折叠 Div 设置动画