📜  如何使用 React 和 framer-motion 创建 Tinder 刷卡手势?(1)

📅  最后修改于: 2023-12-03 14:52:01.770000             🧑  作者: Mango

如何使用 React 和 framer-motion 创建 Tinder 刷卡手势?

在本文中,我们将学习如何使用 React 和 framer-motion 库来创建 Tinder-式的刷卡手势动画效果。在本教程的最终部分,我们会获得一个完整的基于 React 和 framer-motion 库的 Tinder 应用程序。

准备工作

在开始之前,确保你拥有最新版本的 Node.js 和 npm。你可以使用以下命令检查它们的版本:

node -v
npm -v

然后创建一个新的 React 应用程序:

npx create-react-app my-tinder-app
cd my-tinder-app

安装 framer-motion 库:

npm install framer-motion

现在,我们已经准备好开始构建我们的应用程序。

建立卡片堆栈

我们要构建一个卡片堆栈,它可以通过手势向左或向右滑动每个卡片。每个卡片都应该能够反弹并返回到屏幕中心。为了实现这个效果,我们将使用 framer-motion 库中的 motion 组件。

src 文件夹下创建一个名为 CardStack 的新组件,并在其中添加以下代码:

import React from "react";
import { motion } from "framer-motion";

const cardStyle = {
  position: "absolute",
  top: 0,
  left: 0,
  right: 0,
  bottom: 0,
  borderRadius: 10,
  backgroundColor: "white",
  boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.1)",
  padding: "20px",
};

const CardStack = () => {
  return (
    <div style={{ position: "relative", height: "500px" }}>
      <motion.div
        style={cardStyle}
        drag="x"
        dragConstraints={{ left: 0, right: 0 }}
        whileDrag={{ scale: 0.98 }}
      />
    </div>
  );
};

export default CardStack;

现在,我们有了一个简单的卡片堆栈,其中包含一个可以水平拖动的卡片。这里我们设置了限制,使卡片不能在屏幕外拖动,并且在拖动时卡片会稍微缩小一点。

建立数据

现在我们需要一些数据来渲染到我们的卡片堆栈中。在 src 文件夹下创建一个名为 data.js 的新文件,并添加一些示例数据:

export const users = [
  {
    id: 1,
    name: "Samantha",
    age: 23,
    bio: "Hi, I'm Samantha and I love hiking and photography.",
    image: "https://i.pravatar.cc/300?img=1",
  },
  {
    id: 2,
    name: "Alex",
    age: 27,
    bio: "Hey, I'm Alex and I'm a foodie and a gamer.",
    image: "https://i.pravatar.cc/300?img=2",
  },
  {
    id: 3,
    name: "Emily",
    age: 25,
    bio: "Hi there, I'm Emily and I'm an artist and a musician.",
    image: "https://i.pravatar.cc/300?img=3",
  },
  {
    id: 4,
    name: "Michael",
    age: 30,
    bio: "Hey, I'm Michael and I'm a traveler and an adventure seeker.",
    image: "https://i.pravatar.cc/300?img=4",
  },
];

这里我们定义了每个用户的 idnameagebioimage。现在,我们已经准备好将这些数据渲染到我们的卡片堆栈中。

渲染卡片

我们将使用 CardStack 组件来渲染我们的卡片堆栈。首先,我们需要在组件中导入要使用的数据和组件:

import React from "react";
import { motion } from "framer-motion";
import { users } from "./data";
import Card from "./Card";

然后,我们需要将所有用户数据 mapping 到卡片组件中:

const CardStack = () => {
  return (
    <div style={{ position: "relative", height: "500px" }}>
      {users.map((user) => (
        <Card key={user.id} user={user} />
      ))}
    </div>
  );
};

现在我们需要为每个用户创建一个卡片组件。在 src 文件夹下创建一个名为 Card 的新组件,并添加以下代码:

import React from "react";
import { motion } from "framer-motion";

const cardStyle = {
  position: "absolute",
  top: 0,
  left: 0,
  right: 0,
  bottom: 0,
  borderRadius: 10,
  backgroundColor: "white",
  boxShadow: "0px 4px 20px rgba(0, 0, 0, 0.1)",
  padding: "20px",
};

const Card = ({ user }) => {
  return (
    <motion.div
      style={cardStyle}
      drag="x"
      dragConstraints={{ left: 0, right: 0 }}
      whileDrag={{ scale: 0.98 }}
    >
      <img src={user.image} alt={user.name} />
      <h1>{user.name}, {user.age}</h1>
      <p>{user.bio}</p>
    </motion.div>
  );
};

export default Card;

我们在卡片组件中渲染了每个用户的姓名、年龄、个人简介和头像。现在我们可以看到所有卡片被成功渲染到屏幕上。

建立滑动手势

现在我们将添加滑动手势来移动卡片。我们将使用 useMotionValueuseTransform 钩子来指定某个值与手势交互时该如何变化。我们还将使用 useSpringuseTransform 钩子来为在拖动结束时添加反弹效果。

Card 组件中,我们将添加以下代码:

import { useMotionValue, useTransform, useSpring } from "framer-motion";

const Card = ({ user }) => {
  const x = useMotionValue(0);
  const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0]);
  const rotate = useTransform(x, [-200, 0, 200], [-45, 0, 45]);

  const imageOpacity = useTransform(x, [-200, 0, 200], [0, 1, 0.2]);
  const scale = useSpring(useTransform(opacity, [0, 1], [1, 1.1]));

  return (
    <motion.div
      style={{
        ...cardStyle,
        opacity,
        x,
        rotate,
      }}
      drag="x"
      dragConstraints={{ left: 0, right: 0 }}
      whileDrag={{ scale: 0.98 }}
      onDragEnd={(event, info) => {
        const swipe = Math.round(info.offset.x / 200);
        const swipeDirection = swipe === 0 ? 0 : swipe > 0 ? 1 : -1;
        console.log(`Swiped ${swipeDirection}`);
      }}
    >
      <img src={user.image} alt={user.name} style={{ opacity: imageOpacity, scale }} />
      <h1>{user.name}, {user.age}</h1>
      <p>{user.bio}</p>
    </motion.div>
  );
};

在以上代码中,我们钩入了卡片的 xopacityrotate 旋转变换,当我们使用手势滑动卡片时,这些值将相应地更新。我们还为图像添加了不同的 opacityscale 变换,并使用 useSpring 钩子为止在卡片拖动结束时添加了反弹效果。

注意: 我们还通过 onDragEnd 回调捕获了滑动卡片后的位置信息,并通过调用 Math.round(info.offset.x / 200) 计算出滑动方向。这里我们使用 200 作为阈值,如果滑动位置大于 200 ,则认为是向右滑动,否则认为向左滑动。

实现卡片喜欢和不喜欢

现在我们需要在卡片上添加喜欢或不喜欢的图标,以便用户可以指定他们的选择。我们将使用 FontAwesome 图标库中的图标。

Card 组件中添加以下代码:

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTimes, faHeart } from "@fortawesome/free-solid-svg-icons";

const Card = ({ user }) => {
  const x = useMotionValue(0);
  const opacity = useTransform(x, [-200, 0, 200], [0, 1, 0]);
  const rotate = useTransform(x, [-200, 0, 200], [-45, 0, 45]);

  const imageOpacity = useTransform(x, [-200, 0, 200], [0, 1, 0.2]);
  const scale = useSpring(useTransform(opacity, [0, 1], [1, 1.1]));

  return (
    <motion.div
      style={{
        ...cardStyle,
        opacity,
        x,
        rotate,
      }}
      drag="x"
      dragConstraints={{ left: 0, right: 0 }}
      whileDrag={{ scale: 0.98 }}
      onDragEnd={(event, info) => {
        const swipe = Math.round(info.offset.x / 200);
        const swipeDirection = swipe === 0 ? 0 : swipe > 0 ? 1 : -1;
        console.log(`Swiped ${swipeDirection}`);
      }}
    >
      <img src={user.image} alt={user.name} style={{ opacity: imageOpacity, scale }} />
      <h1>{user.name}, {user.age}</h1>
      <p>{user.bio}</p>
      <div style={{ position: "absolute", bottom: "20px", left: "20px" }}>
        <FontAwesomeIcon icon={faTimes} size="2x" color="red" />
      </div>
      <div style={{ position: "absolute", bottom: "20px", right: "20px" }}>
        <FontAwesomeIcon icon={faHeart} size="2x" color="green" />
      </div>
    </motion.div>
  );
};

这段代码添加了两个图标: faTimesfaHeart 。我们将这些图标添加到卡片的顶部和底部,并将红色和绿色作为其颜色。现在,我们已经成功地实现了卡片的喜欢和不喜欢功能。

构建卡片堆栈布局

最后一步是将所有卡片堆放在一起,并使其看起来像是一个卡片堆栈。我们要做的是将每个卡片向上移动,留出较小的部分,并对最后一个卡片进行一些不同的变换。

CardStack 组件中添加以下代码:

const CardStack = () => {
  const stackStyle = {
    position: "relative",
    height: "500px",
    padding: "20px 0",
  };

  const lastCardStyle = {
    ...cardStyle,
    position: "absolute",
    top: "0",
    left: "0",
    right: "0",
    bottom: "0",
    margin: "auto",
  };

  return (
    <div style={stackStyle}>
      {users.slice(0, 3).map((user, index) => (
        <Card key={user.id} user={user} style={{ zIndex: users.length - index }} />
      ))}
      <motion.div style={lastCardStyle} animate={{ scale: 0.9, rotate: 5 }} />
    </div>
  );
};

在以上代码中,我们将卡片数组裁剪为前三个卡片,并将它们向上移动,留下一个较小的部分。我们使用 zIndex 属性定义了每个卡片的层次结构,使其看起来像是一个堆栈。

我们还添加了一个最后一个卡片,它使用 animate 变换缩放和旋转。现在我们已经成功地构建了一个基于 React 和 framer-motion 库的 Tinder 应用程序。

总结

恭喜!你已经学会了如何使用 React 和 framer-motion 库来创建类似 Tinder 的刷卡手势效果。在本教程中,我们学习了如何构建一个卡片堆栈,并呈现用户数据。我们还学习了如何使用手势和反弹效果来移动卡片,并添加了喜欢和不喜欢的功能。最后,我们添加了一个卡片堆叠布局来使它看起来更像是一个真正的 Tinder 应用程序。

完整代码及运行效果请参见:https://codesandbox.io/s/react-framer-motion-tinder-98iol