可折叠卡与反应本机复活
#reactnative #android #ios #animation

在本教程中,我们将使用react-native-reanimated创建一个动画可折叠卡。我们将从提供的模板开始,该模板可以在this GitHub link上找到。该模板包含一个带有lantlist的博览会项目。每个列表项目都有一个图像,标题和描述。我们的目标是使描述使用平稳的动画折叠。

Demo gif

##入门

首先,从github克隆该项目,然后切换到template分支:

git clone https://github.com/dimaportenko/reanimated-collapsable-card-tutorial.git
cd reanimated-collapsable-card-tutorial
git checkout template

添加反应本机复活

我们将使用react-native-reanimated库来创建我们的动画。要添加它,请运行以下命令:

npx expo install react-native-reanimated

然后,您需要更新您的babel.config.js

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    plugins: ['react-native-reanimated/plugin'],
  };
};

执行

在我们的ListItem.tsx中,我们将为可折叠内容的高度添加一个新状态:

const [height, setHeight] = useState(0);
const animatedHeight = useSharedValue(0);

我们计算onLayout回调中的可折叠内容高度:

const onLayout = (event: LayoutChangeEvent) => {
  const onLayoutHeight = event.nativeEvent.layout.height;

  if (onLayoutHeight > 0 && height !== onLayoutHeight) {
    setHeight(onLayoutHeight);
  }
};

我们将为可折叠的内容创建动画样式:

const collapsableStyle = useAnimatedStyle(() => {
  animatedHeight.value = expanded ? withTiming(height) : withTiming(0);

  return {
    height: animatedHeight.value,
  };
}, [expanded]);

我们将把可折叠的内容包裹在Animated.View中:

<Animated.View style={[collapsableStyle, {overflow: 'hidden'}]}>
  <View style={{ position: 'absolute'  }} onLayout={onLayout}>
    <Text style={[styles.details, styles.text]}>{item.details}</Text>
  </View>
</Animated.View>

要使我们的代码更可维护,让我们将CollapsableContainer重构为一个可重复使用的组件:

import React, { useState } from "react";
import { LayoutChangeEvent, View, Text } from "react-native";
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from "react-native-reanimated";

export const CollapsableContainer = ({
  children,
  expanded,
}: {
  children: React.ReactNode;
  expanded: boolean;
}) => {
  const [height, setHeight] = useState(0);
  const animatedHeight = useSharedValue(0);

  const onLayout = (event: LayoutChangeEvent) => {
    const onLayoutHeight = event.nativeEvent.layout.height;

    if (onLayoutHeight > 0 && height !== onLayoutHeight) {
      setHeight(onLayoutHeight);
    }
  };

  const collapsableStyle = useAnimatedStyle(() => {
    animatedHeight.value = expanded ? withTiming(height) : withTiming(0);

    return {
      height: animatedHeight.value,
    };
  }, [expanded]);

  return (
    <Animated.View style={[collapsableStyle, { overflow: "hidden" }]}>
      <View style={{ position: "absolute" }} onLayout={onLayout}>
        {children}
      </View>
    </Animated.View>
  );
};

那么,我们可以在ListItem组件中使用我们的新CollapsableContainer组件:

export const ListItem = ({ item }: { item: ListItemType }) => {
  const [expanded, setExpanded] = useState(false);

  const onItemPress = () => {
    setExpanded(!expanded);
  };

  return (
    <View style={styles.wrap}>
      <TouchableWithoutFeedback onPress={onItemPress}>
        <View style={styles.container}>
          <Image source={{ uri: item.image }} style={styles.image} />
          <View style={styles.textContainer}>
            <Text style={styles.text}>{item.title}</Text>
            <Text style={styles.text}>{item.subtitle}</Text>
          </View>
        </View>
      </TouchableWithoutFeedback>
      <CollapsableContainer expanded={expanded}>
        <Text style={[styles.details, styles.text]}>{item.details}</Text>
      </CollapsableContainer>
    </View>
  );
};

就是这样!您已经成功地使用react-native-reanimated在React Native中成功创建了动画可折叠卡。该动画组件提供了流畅的用户体验,并且可以在应用程序的不同部分重复使用单独的CollapsableContainer组件。愉快的编码! Final code