如何使用 Animated.View 在本机反应中以动画形式展开/折叠文本预览

2024-04-11

我正在创建一个文本组件,默认情况下我希望它是 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 。动画展开和折叠文本的最佳方式是什么?


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

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

另外,如果有人对所使用的弹簧动画感到好奇,这里有一个很好的资源:https://medium.com/kaliberinteractive/how-i-transitioned-from-ease-to-spring-animations-5a09eeca0325 https://medium.com/kaliberinteractive/how-i-transitioned-from-ease-to-spring-animations-5a09eeca0325

https://reactnative.dev/docs/animated#spring 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;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Animated.View 在本机反应中以动画形式展开/折叠文本预览 的相关文章

随机推荐