【问题标题】:How to animate expanding / collapsing a text preview in react native with Animated.View如何使用 Animated.View 在本机反应中动画扩展/折叠文本预览
【发布时间】:2020-08-31 17:08:45
【问题描述】:

我正在创建一个文本组件,我希望默认为 2 行,如果用户点击它,它将展开到全长,如果用户再次点击它,它将折叠回2 行。

到目前为止,我的返回函数中有这样的东西:

<TouchableWithoutFeedback
    onPress={() => {
      toggleExpansion();
    }}
>
  <Animated.View style={[{ height: animationHeight }]}>
    <Text
      style={styles.textStyle}
      onLayout={event => setHeight(event.nativeEvent.layout.height)}
      numberOfLines={numberOfLines}
    >
      {longText}
    </Text>
  </Animated.View>
</TouchableWithoutFeedback>

我的状态变量和 toggleExpansion 函数如下所示:

const [expanded, setExpanded] = useState(false);
const [height, setHeight] = useState(0);
const [numberOfLines, setNumberOfLines] = useState();

const toggleExpansion = () => {
  setExpanded(!expanded);
  if (expanded) {
    setNumberOfLines(undefined);
  } else {
    setNumberOfLines(2);
  }
};

到目前为止,这可以展开和折叠,但我不确定如何设置 Animated.timing 函数来为其设置动画。我尝试过这样的事情:

const animationHeight = useRef(new Animated.Value(0)).current;

useEffect(() => {
  Animated.timing(animationHeight, {
    duration: 1000,
    toValue: height,
    easing: Easing.linear
  }).start();
}, [height]);

但它并没有完全奏效。它根本不显示文本,当我尝试将新的 Animated.Value 初始化为比 2 行高更大的数字(如 50)时,无论我展开和折叠多少次,高度总是被截断为 16 .动画展开和折叠文本的最佳方式是什么?

【问题讨论】:

    标签: typescript react-native react-animated


    【解决方案1】:

    我需要为动态高度组件解决这个问题,文本可以解析为 HTML,因此我们考虑了时髦的格式,例如额外的行。这将使用嵌入的 HTML 展开视图。如果您只是想控制文本布局,您可以通过更改文本道具的状态来重新渲染组件。移除或更改渐变颜色以匹配您的背景。

    组件渲染全文视图并使用“onLayout”侦听器获取高度,初始视图容器设置为静态高度,如果渲染文本视图的完整高度大于初始高度,则显示“阅读更多”按钮,并为切换设置了全高值。

    另外,如果有人对使用的弹簧动画感到好奇,这里有一个很好的资源:@​​987654321@

    https://reactnative.dev/docs/animated#spring

    import React, { useEffect, useState, useRef } from 'react';
    import { 
        Animated,
        StyleSheet,
        Text, 
        TouchableWithoutFeedback,
        View, 
    } from 'react-native';
    import LinearGradient from 'react-native-linear-gradient';
    
    const MoreText = (props) => {
        // const [text, setText] = useState('');
        const startingHeight = 160;
        const [expander, setExpander] = useState(false);
        const [expanded, setExpanded] = useState(false);
        const [fullHeight, setFullHeight] = useState(startingHeight);
        const animatedHeight = useRef(new Animated.Value(startingHeight)).current;
    
    useEffect(() => {
        // expanded?setText(props.text): setText(props.text.substring(0, 40));
        Animated.spring(animatedHeight, {
            friction: 100,
            toValue: expanded?fullHeight:startingHeight,
            useNativeDriver: false
        }).start();
    }, [expanded]);
    
    const onTextLayout = (e) => {
        let {x, y, width, height} = e.nativeEvent.layout;
        height = Math.floor(height) + 40;
        if(height > startingHeight ){
            setFullHeight(height);
            setExpander(true);
        }
    };
    
      return (
        <View style={styles.container}>
            <Animated.View style={[styles.viewPort, { height: animatedHeight }]}>
                <View style={styles.textBox} onLayout={(e) => {onTextLayout(e)}}>
                    <Text style={styles.text}>{props.text}</Text>
                </View>
            </Animated.View>
    
            {expander &&
            <React.Fragment>
                <LinearGradient
                    colors={[
                        'rgba(22, 22, 22,0.0)', // Change this gradient to match BG  
                        'rgba(22, 22, 22,0.7)',               
                        'rgba(22, 22, 22,0.9)',      
                    ]}
                style={styles.gradient}/>
                <TouchableWithoutFeedback onPress={() => {setExpanded(!expanded)}}>
                    <Text style={styles.readBtn}>{expanded?'Read Less':'Read More'}</Text>
                </TouchableWithoutFeedback>
                </React.Fragment>
            }
        </View>
     
      );
    }
    
    const styles = StyleSheet.create({
      absolute: {
        position: "absolute",
        height: 60,
        left: 0,
        bottom: 20,
        right: 0
      },
      container: {
        flex: 1,
      },
      viewPort: {
        flex: 1,
        overflow: 'hidden',
        top: 12,
        marginBottom: 20,
      },
      textBox: {
        flex: 1,
        position: 'absolute',
      },
      text: {
        color: '#fff',
        alignSelf: 'flex-start',
        textAlign: 'justify',
        fontSize: 14,
        fontFamily: 'Avenir',
      },
      gradient:{
        backgroundColor:'transparent', // required for gradient
        height: 40,  
        width: '100%', 
        position:'absolute', 
        bottom: 20
      },
      readBtn: {
        flex: 1,
        color: 'blue',
        alignSelf: 'flex-end',
      },
    });
    
    export default MoreText;

    【讨论】:

    • 我所说的控制文本布局的意思是你可以通过改变文本的状态以编程方式呈现组件,即; expand?setText(props.text):setText('截断的文字放在这里');
    【解决方案2】:

    这解决了你的问题

    import * as React from 'react';
    import { Text, View, StyleSheet, Image,Animated,TouchableWithoutFeedback ,Easing} from 'react-native';
    
    export default function AssetExample() {
    
    const [expanded, setExpanded] = React.useState(true);
    const animationHeight = React.useRef(new Animated.Value(2)).current;
    
    const toggleExpansion = () => {
      setExpanded(!expanded);
    };
    
    React.useEffect(() => {
       if (expanded) {
      Animated.timing(animationHeight, {
        duration: 1000,
        toValue: 60,
        easing: Easing.linear
      }).start();
       }
       else{
         Animated.timing(animationHeight, {
        duration: 1000,
        toValue: 5,
        easing: Easing.linear
      }).start();
      }
    }, [expanded]);
    
      return (
        <View style={styles.container}>
         <TouchableWithoutFeedback
        onPress={() => {
          toggleExpansion();
        }}
    >
      <Animated.View style={[{ height: animationHeight }]}>
          <Text numberOfLines={expanded ? 30 : 2} ellipsizeMode="tail">
          {' line 1'}
        {'\n'}
          {'line 2'}
        {'\n'}
          {'line 3'}
        </Text>
      </Animated.View>
    </TouchableWithoutFeedback>
        </View>
      );
    }
    
    const styles = StyleSheet.create({
      container: {
        alignItems: 'center',
        justifyContent: 'center',
        padding: 24,
      },
     
    });
    
    

    expo

    【讨论】:

    • 没有解释或 cmets 的大代码块可能不会很有帮助,请务必解释您的解决方案是如何工作的,或者至少为代码提供一些文本前奏。
    【解决方案3】:

    这是您问题的解决方案。

    我正在创建一个 View 组件,当您单击它时我有文本,它会展开,如果您再次单击它,它将折叠。

     import React, { Component } from 'react';
     import { Text, View, StyleSheet, LayoutAnimation, Platform, UIManager,TouchableOpacity } from 'react-native';
     export default class App extends Component {
       constructor(){
         super();
          this.state = { expanded: false }
          if (Platform.OS === 'android') {
          UIManager.setLayoutAnimationEnabledExperimental(true);
          }
       }
       changeLayout = () => {
         LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
         this.setState({ expanded: !this.state.expanded });
       }
        render() {
          return ( 
           <View style={styles.container}> 
              <View style={styles.btnTextHolder}> 
                 <TouchableOpacity activeOpacity={0.8} 
                        onPress={this.changeLayout} style={styles.Btn}> 
                    <Text style={styles.btnText}>Expand / Collapse</Text>
                 </TouchableOpacity>
                 <View style={{ height: this.state.expanded ? null : 0,
                        overflow: 'hidden' }}>
                 <Text style={styles.text}>
                    Lorem Ipsum is simply 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>
               </View>
             </View> 
           </View> 
        );
       }
     }
      const styles = StyleSheet.create({
        container: { 
           flex: 1,
           paddingHorizontal: 10,
           justifyContent: 'center',
           paddingTop: (Platform.OS === 'ios') ? 20 : 0 },
        text: { 
           fontSize: 17,
           color: 'black',
           padding: 10 },
        btnText: {
           textAlign: 'center',
           color: 'white',
           fontSize: 20 },
        btnTextHolder: {
           borderWidth: 1,
           borderColor: 'rgba(0,0,0,0.5)' },
        Btn: { 
           padding: 10,
           backgroundColor: 'rgba(0,0,0,0.5)' }
      });
    

    希望它对你有用。

    【讨论】:

      【解决方案4】:

      在 Yoel 的回答上有所改进。 您可以为视图设置动画,在其中添加文本,并在动画结束时添加回调,将文本限制为最多 2 行。

      世博小吃加码:https://snack.expo.io/@lucaskuhn/collapsible-card-

      *关闭折叠时似乎有延迟,如果有人知道如何解决此问题,请告诉我。

      【讨论】:

      • 我认为延迟是因为maxHeight值大于View的实际高度,所以maxHeight值到达View的实际高度需要一段时间。一旦完成,我们就会看到动画生效。
      猜你喜欢
      • 2018-07-06
      • 1970-01-01
      • 2022-11-19
      • 1970-01-01
      • 2021-02-17
      • 2016-01-18
      • 2020-04-07
      • 2021-03-04
      • 1970-01-01
      相关资源
      最近更新 更多