50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | NetflixMobileNavigation(奈飞移动导航组件)
使用Vue 3和Tailwind CSS实现一个具有三层嵌套滑动动画的移动端侧边导航菜单。该菜单通过transform和transition实现视觉冲击效果,当点击菜单按钮时,三个不同颜色的面板会依次滑入(深灰、红色、白色)。
📅 我们继续 50 个小项目挑战!—— NetflixMobileNavigation组件
仓库地址:https://github.com/SunACong/50-vue-projects
项目预览地址:https://50-vue-projects.vercel.app/

欢迎来到本篇教程!今天我们将使用 Vue 3 的 <script setup> 语法结合 Tailwind CSS 来打造一个极具视觉冲击力的移动端侧边导航菜单。这个菜单的亮点在于其独特的三层嵌套滑动动画,当用户点击菜单按钮时,三个不同颜色的面板会依次从屏幕左侧滑入,营造出一种“层层展开”的动感效果,非常适合用于移动应用或响应式网站。
让我们开始吧!🚀
📝 应用目标
- 创建一个响应式的移动端侧边导航菜单
- 实现一个带有延迟的、分层的滑入/滑出动画效果
- 使用 Vue 3 的响应式系统控制菜单的显示状态
- 利用 Tailwind CSS 快速构建现代化的 UI 和基础动画
- 通过
transform和transition类实现复杂的动画序列
🔧 技术实现点
| 技术点 | 描述 |
|---|---|
Vue 3 <script setup> |
使用 ref 定义响应式变量 isNavVisible |
v-model / ref 状态管理 |
isNavVisible 控制菜单的打开和关闭 |
@click 事件监听 |
监听打开和关闭按钮的点击事件 |
:class 动态绑定 |
根据 isNavVisible 的值动态切换 CSS 类,控制面板的 transform 状态 |
transition-transform duration-300 ease-in-out |
定义平滑的位移动画 |
delay-100 / delay-200 |
为内层面板添加动画延迟,实现错开的滑入效果 |
transform (CSS) |
使用 translate-x 实现面板的位移(从 -100% 到 0) |
fixed 定位 |
将菜单面板固定在视口,实现覆盖层效果 |
z-50 / z-index |
控制打开按钮的层级,确保其始终可见 |
📚 常量和路由定义
本组件的核心是其分层动画逻辑。理解以下关键配置至关重要:
| 配置 | 值/说明 |
|---|---|
isNavVisible (响应式) |
布尔值,true 表示菜单打开,false 表示菜单关闭 |
| 外层面板 | w-[60%] (宽度占屏幕60%),bg-gray-900 (深灰色背景) |
| 中层面板 | w-[95%] (相对于外层面板95%宽),bg-red-600 (红色背景),delay-100 (延迟100ms动画) |
| 内层面板 | w-[95%] (相对于中层面板95%宽),bg-white (白色背景),delay-200 (延迟200ms动画) |
| 动画持续时间 | duration-300 (300毫秒) |
| 动画缓动函数 | ease-in-out (先慢后快再慢) |
💡 提示:
delay-100和delay-200是 Tailwind CSS 提供的实用类,分别代表transition-delay: 100ms和200ms。这使得内层面板的动画在触发后稍晚开始,从而形成错落有致的视觉效果。
🖌️ 组件实现
🎨 模板结构 <template>
<template>
<div class="relative flex min-h-screen flex-col items-center justify-center font-sans">
<!-- 打开按钮 -->
<button
class="fixed top-3 left-3 z-50 border-none bg-transparent text-2xl text-white"
@click="isNavVisible = true">
<i class="fas fa-bars"></i>
</button>
<!-- Logo和标题 -->
<img
src="https://images.ctfassets.net/4cd45et68cgf/7LrExJ6PAj6MSIPkDyCO86/542b1dfabbf3959908f69be546879952/Netflix-Brand-Logo.png?w=684&h=456"
alt="Logo"
class="h-auto w-40" />
<p class="mt-4 text-sm text-gray-700 uppercase">Mobile Navigation</p>
<!-- 导航菜单 (三层嵌套) -->
<div
:class="[
'fixed top-0 left-0 h-full transform transition-transform duration-300 ease-in-out',
isNavVisible ? 'translate-x-0' : '-translate-x-full',
]"
class="w-[60%] max-w-[480px] min-w-[320px] bg-gray-900">
<div
:class="[
'h-full transform transition-transform duration-300 ease-in-out',
isNavVisible ? 'translate-x-0 delay-100' : '-translate-x-full',
]"
class="w-[95%] bg-red-600">
<div
:class="[
'h-full transform transition-transform duration-300 ease-in-out',
isNavVisible ? 'translate-x-0 delay-200' : '-translate-x-full',
]"
class="relative w-[95%] bg-white p-10">
<!-- 关闭按钮 -->
<button
class="absolute top-10 right-8 border-none bg-transparent text-xl text-gray-400"
@click="isNavVisible = false">
<i class="fas fa-times"></i>
</button>
<!-- 导航Logo -->
<img
src="https://images.ctfassets.net/4cd45et68cgf/7LrExJ6PAj6MSIPkDyCO86/542b1dfabbf3959908f69be546879952/Netflix-Brand-Logo.png?w=684&h=456"
alt="Logo"
class="mb-8 h-auto w-32" />
<!-- 导航菜单 -->
<ul class="list-none p-0">
<li class="mb-5">
<a
href="#"
class="text-sm text-gray-900 uppercase no-underline transition-colors hover:text-red-600">
Teams
</a>
</li>
<li class="mb-5">
<a
href="#"
class="text-sm text-gray-900 uppercase no-underline transition-colors hover:text-red-600">
Locations
</a>
</li>
<li class="mb-5">
<a
href="#"
class="text-sm text-gray-900 uppercase no-underline transition-colors hover:text-red-600">
Life at Netflix
</a>
</li>
<li>
<ul class="mt-2 list-none pl-5">
<li class="mb-3">
<a
href="#"
class="text-sm text-gray-900 uppercase no-underline transition-colors hover:text-red-600">
Netflix culture memo
</a>
</li>
<li class="mb-3">
<a
href="#"
class="text-sm text-gray-900 uppercase no-underline transition-colors hover:text-red-600">
Work life balance
</a>
</li>
<li class="mb-3">
<a
href="#"
class="text-sm text-gray-900 uppercase no-underline transition-colors hover:text-red-600">
Inclusion & diversity
</a>
</li>
<li class="mb-3">
<a
href="#"
class="text-sm text-gray-900 uppercase no-underline transition-colors hover:text-red-600">
Blog
</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
模板结构清晰地分为三个主要部分:
-
触发按钮:
- 一个固定在屏幕左上角 (
fixed top-3 left-3) 的按钮,使用 Font Awesome 的fa-bars图标。 z-50确保它在任何滑出的菜单面板之上。- 点击时 (
@click) 将isNavVisible设置为true,触发菜单展开。
- 一个固定在屏幕左上角 (
-
主页面内容:
- 居中的 Netflix Logo 和一个标题 “Mobile Navigation”,用于展示菜单未展开时的主界面。
-
三层嵌套导航菜单:
- 外层 (
div):最基础的容器,fixed定位,w-[60%]宽度,bg-gray-900背景。其:class绑定根据isNavVisible决定是translate-x-0(完全显示) 还是-translate-x-full(完全隐藏在屏幕外)。 - 中层 (
div):嵌套在外层之内,w-[95%]宽度,bg-red-600背景。它的动画有delay-100,意味着当外层开始滑入后100ms,它才开始自己的滑入动画。 - 内层 (
div):最内层的面板,包含实际的导航内容。w-[95%]宽度,bg-white背景,p-10内边距。它的动画有delay-200,比中层再晚100ms开始。 - 关闭按钮:位于内层面板右上角的
fa-times图标,点击后将isNavVisible设置为false,触发动画反向执行,菜单逐层收起。 - 导航内容:一个包含主菜单项和子菜单项的无序列表 (
ul),使用hover:text-red-600实现鼠标悬停时的文字颜色变化。
- 外层 (
💻 脚本逻辑 <script setup>
<script setup>
import { ref } from 'vue'
// 响应式状态管理导航显示/隐藏
const isNavVisible = ref(false)
</script>
脚本部分极其简洁,体现了 Vue 3 Composition API 的优雅:
isNavVisible:一个由ref创建的响应式布尔变量,初始值为false,表示菜单默认是关闭的。- 这个变量被模板中的
:class绑定和@click事件直接引用,实现了视图与状态的双向绑定。当状态改变时,Vue 会自动重新计算并应用相应的 CSS 类,从而触发动画。
🎨 Tailwind CSS 样式重点
| 类名 | 作用 |
|---|---|
relative / fixed |
定位方式,fixed 使元素脱离文档流并相对于视口定位 |
top-0 / left-0 / top-3 / right-8 / top-10 |
精确控制元素位置 |
h-full |
高度100% |
w-[60%] / w-[95%] / w-40 / w-32 |
宽度设置(支持任意值) |
max-w-[480px] / min-w-[320px] |
最大/最小宽度限制 |
flex / flex-col |
Flexbox 布局 |
items-center / justify-center |
Flex 项目对齐 |
min-h-screen |
最小高度为视口高度 |
font-sans |
无衬线字体 |
border-none / bg-transparent |
移除边框和背景 |
text-2xl / text-xl / text-sm / text-white / text-gray-700 / text-gray-900 / text-gray-400 / text-red-600 |
文字大小和颜色 |
uppercase |
文字大写 |
z-50 |
层级最高 |
transform |
启用 CSS 变换 |
transition-transform / transition-colors / transition-all |
指定需要过渡的CSS属性 |
duration-300 |
过渡持续时间为300ms |
ease-in-out |
过渡缓动函数 |
delay-100 / delay-200 |
过渡延迟100ms/200ms |
translate-x-0 / -translate-x-full |
X轴位移(0% / -100%) |
bg-gray-900 / bg-red-600 / bg-white |
背景颜色 |
p-10 / pl-5 / mb-5 / mb-3 / mb-8 / mt-2 |
内边距和外边距 |
list-none |
移除列表默认样式(项目符号) |
no-underline |
移除下划线 |
hover:text-red-600 |
悬停时文字变红 |
absolute |
绝对定位(用于关闭按钮) |
h-auto |
高度自适应 |
📁 常量定义 + 组件路由
constants/index.js 添加组件预览常量:
{
id: 45,
title: 'NetflixMobileNavigation',
image: 'https://50projects50days.com/img/projects-img/45-netflix-mobile-navigation.png',
link: 'NetflixMobileNavigation',
},
router/index.js 中添加路由选项:
{
path: '/NetflixMobileNavigation',
name: 'NetflixMobileNavigation',
component: () => import('@/projects/NetflixMobileNavigation.vue'),
},
🏁 总结
在这篇教程中,我们成功创建了一个视觉效果惊艳的移动端侧边导航菜单。我们巧妙地利用了 Vue 3 的响应式系统和 Tailwind CSS 的 transform、transition 以及 delay 实用类,通过三层嵌套和延迟动画,实现了富有层次感和动感的交互效果。
这个导航组件已经很吸引人,但还可以进一步增强:
- ✅ 遮罩层:在菜单展开时,在菜单背后添加一个半透明的遮罩层 (
<div class="fixed inset-0 bg-black bg-opacity-50"></div>),点击遮罩层也能关闭菜单。 - ✅ 键盘支持:监听
Escape键来关闭菜单。 - ✅ 更复杂的动画:结合
scale或rotate变换,创建更炫酷的展开/收起效果。 - ✅ 响应式优化:调整
w-[60%]等宽度值,使其在不同设备上表现更佳。 - ✅ 动态内容:将导航菜单数据 (
<li>元素) 提取到一个ref或reactive对象中,便于管理和国际化。
👉 下一篇,我们将完成QuizApp组件,一个适合问卷调查的问答组件。🚀
感谢阅读,欢迎点赞、收藏和分享 😊
更多推荐



所有评论(0)