Tailwind CSS + React 实现炫酷 3D 卡片翻转动画 | 完整教程
发布日期:2023-08-27 修改时间:2023-08-27 阅读所需:4 分钟
tailwind css
react
css
animation
transform
最近重新复习了一下在 CSS 中有关于 Transform(变换)相关的属性以及用法,这里就简单介绍一下如何通过核心代码来实现一个卡片正反面翻转效果。

关键要点
容器处理
- 设置相对定位
position: relative;
- 设置透视距离
perspective: 1000px;
并开启 3d 变换效果transform-style: preserve-3d;
- 设置过渡动画及延时
transition: transform 700ms;
让翻转效果更佳明显; - 设置 Y 轴角度旋转,如当鼠标悬停或点击时翻转 180 度
transform: rotateY(180deg);
。
卡片正面(Front Face)和反面(Back Face)的处理
- 正反面子元素统一设置绝对定位
position: absolute;
- 为正反面子元素设置在透视时隐藏的
backface-visiblity: hidden;
属性 - 将卡片背面(Back Face)设置 Y 轴旋转 180 度效果
transform: rotateY(180deg);
,而卡片正面(front face)由于是默认展示则不需要设置旋转,而由于背面已经事先旋转了 180 度,于是容器从正面切换到背面时,背面相当于旋转了 360 度此时呈现的就是正面
为了方便实现翻转状态管理,这里我使用了 React 并搭配 Tailwind CSS 来完成最终实现。
实现
除了 React 和 Tailwind CSS 之外还额外使用了 tailwind-merge 用于帮我根据状态切换不同的样式。
React 与 Tailwind CSS
这里我主要通过 Vite 来帮助快速搭建一个简单的脚手架,然后安装相关依赖即可:
pnpm ipnpm add -D tailwind-merge
Tailwind CSS 设置可参考 官方文档。
最终实现如下:
import { useState } from "react";import { twMerge as tw } from "tailwind-merge";
export default function FlippableCard() { const [hasFlipped, setHasFilpped] = useState(false);
return ( <div className="flex items-center justify-center min-h-screen"> <div className={tw( "relative w-[240px] h-[360px] cursor-pointer", "transition-transform duration-700 perspective-1000", "transform-style-3d perspective-origin-center", hasFlipped && "rotate-y-180" )} onClick={() => setHasFilpped(!hasFlipped)} > <div className={tw( "absolute inset-0 top-0 left-0", "flex flex-col items-center justify-center w-full h-full p-2", "rounded-lg shadow-[0_10px_30px_-15px_rgba(0,0,0,0.2)]", "bg-gradient-to-b from-sky-200/50 via-purple-100 to-slate-50 ", "backdrop-blur-lg backface-hidden" )} > <div className="font-mono text-lg">Front Face</div> </div> <div className={tw( "absolute inset-0 top-0 left-0", "flex flex-col items-center justify-center w-full h-full p-2", "rounded-lg shadow-[0_10px_30px_-15px_rgba(0,0,0,0.2)]", "bg-gradient-to-b from-sky-200/50 via-purple-100 to-slate-50 ", "backdrop-blur-lg backface-hidden rotate-y-180" )} > <div className="font-mono text-lg">Back Face</div> </div> </div> </div> );}
原生 HTML 与 CSS
当然同样附上对应的原生 HTML5 与 CSS3 代码(基于 ChatGPT 转换):
<!DOCTYPE html><html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> .container { display: flex; min-height: 100vh; min-width: 100vw; align-items: center; justify-content: center; }
.card { position: relative; width: 240px; height: 360px; cursor: pointer; transition: transform 700ms; transform-style: preserve-3d; perspective: 1000px; perspective-origin: center; }
.card.is-flipped { transform: rotateY(180deg); }
.front-face, .back-face { position: absolute; inset: 0; top: 0; left: 0; display: flex; flex-direction: column; justify-content: center; align-items: center; width: 100%; height: 100%; padding: 2px; border-radius: 8px; box-shadow: 0 10px 30px -15px rgba(0, 0, 0, 0.2); backdrop-filter: blur(8px); -webkit-backface-visibility: hidden; backface-visibility: hidden; background-image: linear-gradient(to bottom, rgba(135, 206, 235, 0.5), rgba(147, 112, 219, 0.5), rgba(119, 128, 144, 0.5)); }
.back-face { transform: rotateY(180deg); }
.text { font-family: monospace; font-size: 18px; } </style></head>
<body> <div class="container"> <div id="card" class="card"> <div class="front-face"> <div class="text">Front Face</div> </div> <div class="back-face"> <div class="text">Back Face</div> </div> </div> </div>
<script> const card = document.getElementById("card"); let isFlipped = false;
card.addEventListener("click", () => { isFlipped = !isFlipped; card.classList.toggle("is-flipped", isFlipped); }); </script></body>
</html>