50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Form Wave(表单label波动效果)

news2025/7/24 4:21:29

📅 我们继续 50 个小项目挑战!—— FormWave组件

仓库地址:https://github.com/SunACong/50-vue-projects

项目预览地址:https://50-vue-projects.vercel.app/

在这里插入图片描述


🎯 组件目标

构建一个美观、动态的登录表单,重点在于实现带有浮动标签(floating label)的输入框体验,提升交互感知和视觉效果,适合作为任何登录注册模块的基础模板。

🛠️ 技术实现点

  • 使用 Vue3 + <script setup> 编写响应式逻辑。
  • 使用 TailwindCSS 完全控制样式,特别是浮动文字的动画。
  • 实现 focus/blur 状态下标签文字的动画浮动效果。
  • 使用 v-model 实现双向绑定,结合 focus 状态精准控制浮动逻辑。

🧱 组件实现

<!-- 🌈 模板部分 Template -->
<template>
    <div class="flex h-screen items-center justify-center bg-gray-800 text-gray-300">
        <div class="rounded-2xl bg-gray-500/60 p-12 text-center">
            <h1 class="text-4xl font-bold text-gray-300">Please Login</h1>
            <form>
                <!-- Email 输入框 -->
                <div class="form-control relative mt-10 border-b-2 border-b-white">
                    <input
                        class="peer relative z-10 w-full bg-transparent py-3 text-white focus:border-sky-300 focus:outline-none"
                        type="text"
                        required
                        v-model="emailValue"
                        @focus="activeInput = 'email'"
                        @blur="handleBlur('email')" />
                    <label class="pointer-events-none absolute top-4 left-0">
                        <!-- ✨ 字符级浮动动画 -->
                        <span
                            v-for="(letter, idx) in 'Email'.split('')"
                            :key="idx"
                            :class="[
                                'inline-block min-w-[5px] text-lg transition-all duration-300',
                                'transform-gpu',
                                {
                                    '-translate-y-8 text-sky-300':
                                        activeInput === 'email' || emailValue,
                                },
                            ]"
                            :style="{
                                transitionDelay: `${idx * 50}ms`,
                                transitionTimingFunction: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
                            }">
                            {{ letter }}
                        </span>
                    </label>
                </div>

                <!-- Password 输入框 -->
                <div class="form-control relative mt-10 border-b-2 border-b-white">
                    <input
                        class="peer relative z-10 w-full bg-transparent py-3 text-white focus:border-sky-300 focus:outline-none"
                        type="password"
                        required
                        v-model="passwordValue"
                        @focus="activeInput = 'password'"
                        @blur="handleBlur('password')" />
                    <label class="pointer-events-none absolute top-4 left-0">
                        <!-- ✨ 字符级浮动动画 -->
                        <span
                            v-for="(letter, idx) in 'Password'.split('')"
                            :key="idx"
                            :class="[
                                'inline-block min-w-[5px] text-lg transition-all duration-300',
                                'transform-gpu',
                                {
                                    '-translate-y-8 text-sky-300':
                                        activeInput === 'password' || passwordValue,
                                },
                            ]"
                            :style="{
                                transitionDelay: `${idx * 50}ms`,
                                transitionTimingFunction: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
                            }">
                            {{ letter }}
                        </span>
                    </label>
                </div>

                <!-- 登录按钮 -->
                <button
                    class="mt-10 w-full rounded bg-blue-500 px-4 py-2 font-bold hover:bg-blue-600 focus:ring-2 focus:ring-blue-400 focus:outline-none"
                    type="submit">
                    Login
                </button>

                <p class="mt-10">
                    Don't have an account?
                    <a href="#" class="text-blue-400 hover:underline">Register</a>
                </p>
            </form>
        </div>
    </div>
</template>


🧩 重点效果实现

<!-- ⚙️ 脚本部分 Script -->
<script setup>
import { ref } from 'vue'

// 输入值响应式变量
const emailValue = ref('')
const passwordValue = ref('')

// 当前聚焦的输入项
const activeInput = ref(null)

// 失焦逻辑:如果输入框为空,则取消浮动状态
const handleBlur = (inputName) => {
    if (
        (inputName === 'email' && !emailValue.value) ||
        (inputName === 'password' && !passwordValue.value)
    ) {
        activeInput.value = null
    }
}
</script>
  • 标签浮动是通过 translate-y 配合 activeInput 或绑定值来实现的。
  • 使用 transition-delay 实现了字符级别的延迟动画,让文字一个个浮动。
  • 利用 cubic-bezier 定义自定义缓动函数,提升动画的弹性和自然感。

🎨 TailwindCSS 样式重点讲解

类名作用
peer / relative z-10确保 input 在 label 之上,供 label 状态判断使用
-translate-y-8控制文字上浮距离
transition-delay实现文字一个个浮动的动画延迟
transform-gpu使用 GPU 加速动画,提高性能和流畅度
focus:outline-none / focus:ring-2聚焦时视觉反馈
min-w-[5px]保证字符宽度一致,不会断行

🧾 常量定义 + 组件路由建议

constants/index.js 添加组件预览常量:

{
        id: 8,
        title: 'Form Wave',
        image: 'https://50projects50days.com/img/projects-img/8-form-wave.png',
        link: 'FormWave',
    },

router/index.js 中添加路由选项:

{
        path: '/FormWave',
        name: 'FormWave',
        component: () => import('@/projects/FormWave.vue'),
    },

路由守卫可后续扩展身份验证逻辑,跳转到注册或首页。


🧠 小结

完成了通用场景下的表单样式界面,可以为你以后的表单设计以及登录页面提供一些灵感进行参考!!!🚀


👉 下一篇,我们将完成声音组件 Sound Board组件,可以实现点击发出对应的声音!🚀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2397454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

双目相机深度的误差分析(基线长度和相机焦距的选择)

全文基于针孔模型和基线水平放置来讨论 影响双目计算深度的因素&#xff1a; 1、基线长度&#xff1a;两台相机光心之间距离2、相机焦距&#xff08;像素&#xff09;&#xff1a; f x f_x fx​&#xff08;或 f y f_y fy​&#xff09;为焦距 f f f和一个缩放比例的乘积。在…

Pytorch Geometric官方例程pytorch_geometric/examples/link_pred.py环境安装教程及图数据集制作

最近需要训练图卷积神经网络&#xff08;Graph Convolution Neural Network, GCNN&#xff09;&#xff0c;在配置GCNN环境上总结了一些经验。 我觉得对于初学者而言&#xff0c;图神经网络的训练会有2个难点&#xff1a; ①环境配置 ②数据集制作 一、环境配置 我最初光想…

React---day6、7

6、组件之间进行数据传递 **6.1 父传子&#xff1a;**props传递属性 父组件&#xff1a; <div><ChildCpn name"蒋乙菥" age"18" height"1,88" /> </div>子组件&#xff1a; export class ChildCpn extends React.Component…

hook组件-useEffect、useRef

hook组件-useEffect、useRef useEffect 用法及执行机制 WillMount -> render -> DidMount ShouldUpdate -> WillUpdate -> render -> DidUpdate WillUnmount(只有这个安全) WillReceiveProps useEffect(callback) 默认所有依赖都更新useEffect(callback, [])&am…

随机游动算法解决kSAT问题

input&#xff1a;n个变量的k-CNF公式 ouput&#xff1a;该公式的一组满足赋值或宣布没有满足赋值 算法步骤&#xff1a; 随机均匀地初始化赋值 a ∈ { 0 , 1 } n a\in\{0,1\}^n a∈{0,1}n.重复t次&#xff08;后面会估计这个t&#xff09;&#xff1a; a. 如果在当前赋值下…

《Discuz! X3.5开发从入门到生态共建》第1章 Discuz! 的前世今生-优雅草卓伊凡

《Discuz! X3.5开发从入门到生态共建》第1章 Discuz! 的前世今生-优雅草卓伊凡 第一节 从康盛创想到腾讯收购&#xff1a;PC时代的辉煌 1.1 Discuz! 的诞生&#xff1a;康盛创想的开源梦想 2001年&#xff0c;中国互联网正处于萌芽阶段&#xff0c;个人网站和论坛开始兴起。…

笔试强训:Day6

一、小红的口罩&#xff08;贪心优先级队列&#xff09; 登录—专业IT笔试面试备考平台_牛客网 #include<iostream> #include<queue> #include<vector> using namespace std; int n,k; int main(){//用一个小根堆 每次使用不舒适度最小的cin>>n>&…

谷歌Stitch:AI赋能UI设计,免费高效新利器

在AI技术日新月异的今天&#xff0c;各大科技巨头都在不断刷新我们对智能工具的认知。最近&#xff0c;谷歌在其年度I/O开发者大会期间&#xff0c;除了那些聚光灯下的重磅发布&#xff0c;还悄然上线了一款令人惊喜的AI工具——Stitch。这是一款全新的、完全免费的AI驱动UI&am…

运营商地址和ip属地一样吗?怎么样更改ip属地地址

‌在互联网时代&#xff0c;IP属地和运营商地址是两个经常被提及的概念&#xff0c;但它们是否相同&#xff1f;如何更改IP属地地址&#xff1f;这些问题困扰着许多网民。本文将深入探讨这两个概念的区别&#xff0c;并详细介绍更改IP属地地址的方法。 一、运营商地址和IP属地一…

在QT中,利用charts库绘制FFT图形

第1章 添加charts库 1.1 .pro工程添加chart库 1.1.1 在.pro工程里面添加charts库 1.1.2 在需要使用的地方添加这两个库函数&#xff0c;顺序一点不要搞错&#xff0c;先添加.pro&#xff0c;否则编译器会找不到这两个.h文件。 第2章 Charts关键绘图函数 2.1 QChart 类 QChart 是…

流媒体协议分析:流媒体传输的基石

在流媒体传输过程中&#xff0c;协议的选择至关重要&#xff0c;它决定了数据如何封装、传输和解析&#xff0c;直接影响着视频的播放质量和用户体验。本文将深入分析几种常见的流媒体传输协议&#xff0c;探讨它们的特点、应用场景及优缺点。 协议分类概述 流媒体传输协议根据…

vscode中让文件夹一直保持展开不折叠

vscode中让文件夹一直保持展开不折叠 问题 很多小伙伴使用vscode发现空文件夹会折叠显示, 让人看起来非常难受, 如下图 解决办法 首先打开设置->setting, 搜索compact Folders, 去掉勾选即可, 如下图所示 效果如下 看起来非常爽 ! ! !

JAVA-springboot整合Mybatis

SpringBoot从入门到精通-第15章 MyBatis框架 学习MyBatis心路历程 2022年学习java基础时候&#xff0c;想着怎么使用java代码操作数据库&#xff0c;咨询了项目上开发W同事&#xff0c;没有引用框架&#xff0c;操作数据库很麻烦&#xff0c;就帮我写好多行代码&#xff0c;就…

深度学习pycharm debug

深度学习中&#xff0c;Debug 是定位并解决代码逻辑错误&#xff08;如张量维度不匹配&#xff09;、训练异常&#xff08;如 Loss 波动&#xff09;、数据问题&#xff08;如标签错误&#xff09;的关键手段&#xff0c;通过打印维度、可视化梯度等方法确保模型正常运行、优化…

MicroPython+L298N+ESP32控制电机转速

要使用MicroPython控制L298N电机驱动板来控制电机的转速&#xff0c;你可以通过PWM&#xff08;脉冲宽度调制&#xff09;信号来调节电机速度。L298N是一个双H桥驱动器&#xff0c;可以同时控制两个电机的正反转和速度。 硬件准备&#xff1a; 1. L298N 电机控制板 2. ESP32…

在部署了一台mysql5.7的机器上部署mysql8.0.35

在已部署 MySQL 5.7 的机器上部署 MySQL 8.0.35 的完整指南 在同一台服务器上部署多个 MySQL 版本需要谨慎规划&#xff0c;避免端口冲突和数据混淆。以下是详细的部署步骤&#xff1a; 一、规划配置 端口分配 MySQL 5.7&#xff1a;使用默认端口 3306MySQL 8.0.35&#xff1…

QT入门学习(一)---新建工程与、信号与槽

一: 新建QT项目 二:QT文件构成 2.1 first.pro 项目管理文件&#xff0c;下面来看代码解析 QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c11TARGET main# The following define makes your compiler emit warnings if you use # any Qt feature …

UE5.4.4+Rider2024.3.7开发环境配置

文章目录 一、UE5安装 安装有两种方式一种的源码编译安装、一种是EPIC安装&#xff0c;推荐后者&#xff0c;只需要注册一个EPIC账号就可以一键安装。 二、C环境安装 1.下载VisualStudioSetup 下载链接如下下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux 选择社…

Windows环境下PHP,在PowerShell控制台输出中文乱码

解决方法&#xff1a; 以管理员运行PowerShell , 输入&#xff1a; chcp 65001 重启控制台&#xff1b;然后就正常输出中文&#xff1b;

性能优化 - 理论篇:性能优化的七类技术手段

文章目录 Pre引言性能优化的七类技术手段性能优化策略一览表1. 复用优化2. 计算优化2.1 并行执行2.2 变同步为异步2.3 惰性加载 3. 结果集优化3.1 数据格式与协议选择3.2 字段精简与按需返回3.3 批量处理与分页3.4 索引与位图加速 4. 资源冲突优化4.1 锁的分类与特点4.2 无锁与…