📅 今天我们继续 50 个小项目挑战!------ThreeDBackgroundBoxes组件
仓库地址:https://gitee.com/hhm-hhm/50days50projects.git


创建一个充满魔法感的交互式动画组件 ------ "魔法盒子"。当你点击"Magic"按钮时,整个盒子阵列会放大并旋转,带来一种神奇的视觉体验!
准备好施展魔法了吗?让我们开始吧!✨
🌀 组件目标
- 动态生成 4×4 的盒子网格
- 实现点击按钮触发整体缩放与旋转动画
- 利用背景定位实现 Gif 动画的分块显示
- 添加 3D 边框效果增强立体感
- 采用 Tailwind CSS 快速构建美观的响应式布局
🔧 ThreeDBackgroundBoxes.tsx组件实现
TypeScript
import React, { useState, useEffect } from 'react'
// 定义 Box 类型
interface Box {
x: number
y: number
}
const ThreeDBackgroundBoxes: React.FC = () => {
const [isBig, setIsBig] = useState<boolean>(false)
const [boxes, setBoxes] = useState<Box[]>([])
// 创建 4x4 盒子数据
const createBoxes = () => {
const boxArray: Box[] = []
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
boxArray.push({
x: -j * 125,
y: -i * 125,
})
}
}
setBoxes(boxArray)
}
// 组件挂载时初始化盒子
useEffect(() => {
createBoxes()
}, [])
return (
<div className="flex h-screen flex-col items-center justify-center overflow-hidden bg-gray-800">
{/* 魔法按钮 */}
<button
onClick={() => setIsBig(!isBig)}
className="font-poppins fixed top-5 z-50 transform cursor-pointer rounded bg-yellow-400 px-5 py-3 text-white shadow-md transition-all duration-200 hover:shadow-lg active:translate-y-1 active:shadow-none">
Magic 🎩
</button>
{/* 盒子容器 */}
<div
className={`relative flex flex-wrap justify-around transition-all duration-400 ${
isBig ? 'h-[600px] w-[600px]' : 'h-[500px] w-[500px]'
}`}>
{/* 动态生成盒子 */}
{boxes.map((box, index) => (
<div
key={index}
className={`relative h-[125px] w-[125px] transition-all duration-400 ${
isBig ? 'rotate-360' : 'rotate-0'
}`}
style={{
backgroundImage:
'url(https://media.giphy.com/media/EZqwsBSPlvSda/giphy.gif)',
backgroundRepeat: 'no-repeat',
backgroundSize: '500px 500px',
backgroundPosition: `${box.x}px ${box.y}px`,
}}>
{/* 3D 效果伪元素 ------ 使用额外 div 模拟 */}
<div className="absolute top-2 right-[-15px] h-full w-4 skew-y-12 transform bg-yellow-200" />
<div className="absolute bottom-[-15px] left-2 h-4 w-full skew-x-12 transform bg-yellow-400" />
</div>
))}
</div>
<div className="fixed right-20 bottom-5 z-100 text-2xl text-red-500">
CSDN@Hao_Harrision
</div>
</div>
)
}
export default ThreeDBackgroundBoxes
🔄 关键差异总结
| 功能 | Vue 3 | React + TS |
|---|---|---|
| 响应式状态 | ref() |
useState() |
| 生命周期 | onMounted |
useEffect(..., []) |
| 列表渲染 | v-for |
.map() |
| 条件类名 | :class="[...]" |
模板字符串 + 三元 |
| 动态样式 | :style |
style={``{}} |
| 类型安全 | --- | interface Box + 泛型 |
✅ 使用前检查
- 确保 Tailwind 支持任意值 (如
h-[600px],rotate-[360deg])------默认支持; - 确认 GIF 可访问,否则替换为本地资源;
- 若使用
font-poppins,需在项目中引入 Poppins 字体(Google Fonts 或本地)。
🔁 转换说明
1. 状态管理:ref → useState
| Vue | React |
|---|---|
const isBig = ref(false) const boxes = ref([]) |
const [isBig, setIsBig] = useState(false) const [boxes, setBoxes] = useState<Box[]>([]) |
✅ 使用 TypeScript 泛型 Box[] 提供类型安全。
2. 生命周期:onMounted → useEffect
| Vue | React |
|---|---|
onMounted(() => { createBoxes() }) |
useEffect(() => { createBoxes(); }, []) |
✅ 空依赖数组 [] 确保只在组件挂载时执行一次,等价于 onMounted。
3. 列表渲染:v-for → .map()
- Vue:
<div v-for="(box, index) in boxes" :key="index"> - React:
{boxes.map((box, index) => <div key={index}>...)}
✅ 由于 boxes 是静态生成(4x4 固定),使用 index 作为 key 是安全的。
4. 动态类名与样式
动态尺寸和旋转:
TypeScript
className={`... ${isBig ? 'h-[600px] w-[600px]' : 'h-[500px] w-[500px]'}`}
TypeScript
className={`${isBig ? 'rotate-[360deg]' : 'rotate-0'}`}
⚠️ 注意:Tailwind 默认不支持
rotate-360,需使用 任意值语法rotate-[360deg]。
动态背景位置(inline style):
TypeScript
style={{
backgroundPosition: `${box.x}px ${box.y}px`,
}}
✅ 与 Vue 的 :style 行为完全一致。
5. 事件处理:@click → onClick
TypeScript
onClick={() => setIsBig(!isBig)}
✅ 简洁且符合 React 习惯。
6. 3D 伪元素模拟
Vue 和 React 都无法直接使用 ::before / ::after 通过 Tailwind 控制(因 Tailwind 不作用于伪类,除非启用插件)。
因此,两者都采用 额外 <div> 元素 模拟 3D 边效果:
- 右侧黄条:
right-[-15px],skew-y-12 - 底部黄条:
bottom-[-15px],skew-x-12
✅ 视觉效果完全一致。
7. GIF 背景问题提醒 ⚠️
你使用的 GIF 地址:
bash
https://media.giphy.com/media/EZqwsBSPlvSda/giphy.gif
但在你之前上传的文件中显示:
Access to media.giphy.com was denied --- HTTP ERROR 403
这意味着 该图片可能无法加载(Giphy 限制了外站引用)。
🔧 建议:
-
下载 GIF 并放入
public/目录,改用本地路径:TypeScriptbackgroundImage: 'url(/giphy.gif)' -
或替换为可公开访问的图片 URL。
💡 优化建议(可选)
- 将
boxes逻辑提取为自定义 Hook(如useBoxesGrid); - 使用
useMemo缓存boxes(但此处由useEffect初始化,非必需); - 添加
key={box.x}-${box.y}提升 key 唯一性(虽非必要,但更规范)。
🎨 TailwindCSS 样式重点讲解
| 类名 | 作用 |
|---|---|
h-screen |
设置高度为视口高度 |
flex-col |
垂直方向 Flex 布局 |
items-center / justify-center |
居中对齐 |
overflow-hidden |
隐藏溢出内容 |
bg-gray-100 |
设置背景色 |
fixed top-5 |
按钮固定在顶部 |
z-50 |
提升按钮层级 |
transform |
启用变换 |
rounded |
圆角按钮 |
bg-yellow-400 / text-white |
按钮颜色 |
shadow-md / hover:shadow-lg |
阴影效果 |
active:translate-y-1 |
按下时向下移动 |
active:shadow-none |
按下时隐藏阴影 |
relative / flex-wrap |
容器布局 |
transition-all duration-400 |
0.4s 平滑过渡 |
h-[125px] w-[125px] |
固定盒子尺寸 |
rotate-360 / rotate-0 |
控制旋转状态 |
absolute top-2 right-[-15px] |
创建右侧斜面 |
skew-y-12 |
Y轴倾斜12度 |
bg-yellow-200 / bg-yellow-400 |
3D边框颜色 |
| [🎯 TailwindCSS 样式说明] |
🦌 路由组件 + 常量定义
router/index.tsx 中 children数组中添加子路由
TypeScript
{
path: '/',
element: <App />,
children: [
...
{
path: '/ThreeDBackgroundBoxes',
lazy: () =>
import('@/projects/ThreeDBackgroundBoxes').then((mod) => ({
Component: mod.default,
})),
},
],
},
constants/index.tsx 添加组件预览常量
TypeScript
import demo40Img from '@/assets/pic-demo/demo-40-1.png'
省略部分....
export const projectList: ProjectItem[] = [
省略部分....
{
id: 40,
title: 'ThreeDBackgroundBoxes',
image: demo40Img,
link: 'ThreeDBackgroundBoxes',
},
]
🚀 小结
通过这篇文章,我们使用 React19 和 TailwindCSS 创建一个充满魔法感的交互式动画组件 ------ "魔法盒子"。当你点击"Magic"按钮时,整个盒子阵列会放大并旋转.
想让你的魔法盒子更炫酷?试试这些扩展:
- ✅ 添加音效:点击时播放魔法音效
- ✅ 随机旋转角度:每个盒子旋转角度不同
- ✅ 颜色主题切换:支持多种配色方案
- ✅ 手势控制:支持触摸滑动触发动画
- ✅ 粒子特效:点击时释放小星星或火花动画
📅 明日预告: 我们将完成VerifyAccountUi组件,实现了一个非常使用的验证码UI组件。🚀
感谢阅读,欢迎点赞、收藏和分享 😊
原文链接:https://blog.csdn.net/qq_44808710/article/details/149778543
每天造一个轮子,码力暴涨不是梦!🚀