前言
大家好,在現代 Web 開發中,用户體驗越來越重要。一個簡潔、美觀又富有動效的登錄頁,不僅能提升品牌形象,還能增強用户的第一印象。這篇文章,我們將用 Vue3 + CSS3 動畫,來實現一個懸浮動畫和細膩交互效果的 Vue3 登錄頁面。
項目效果預覽:
- • 登錄框懸浮微動 + 高光掠過動畫
- • 輸入框標籤滑動 + 聚焦反饋
- • 按鈕點擊漣漪動效
- • 浮動光點循環上升
- • 忘記密碼/註冊鏈接懸停切換背景色
技術棧
- • Vue 3(
<script setup>語法) - • Composition API(
ref,onMounted) - • CSS3 漸變、動畫、過渡、偽元素
- • 響應式佈局基礎
一、頁面結構(Template 部分)
<template>
<div class="login-page" @mousemove="handleMouseMove">
<div class="login-container">
<!-- 浮動裝飾圖標 -->
<div class="floating-icons">
<span
v-for="(icon, index) in icons"
:key="index"
:style="{
left: icon.left + 'px',
transform: translateY( icon.top + 'px',
width: icon.size + 'px',
height: icon.size + 'px',
animationDuration: (10 + Math.random() * 20) + 's',
animationDelay: (Math.random() * 5) + 's'
}"
></span>
</div>
<!-- 標題 -->
<h2>歡迎登錄</h2>
<!-- 登錄表單 -->
<form @submit.prevent="handleSubmit">
<!-- 用户名輸入 -->
<div class="input-group">
<input type="text" required v-model="username">
<label>用户名</label>
</div>
<!-- 密碼輸入 -->
<div class="input-group">
<input type="password" required v-model="password">
<label>密碼</label>
</div>
<!-- 登錄按鈕 -->
<button type="submit" class="btn">登 錄</button>
<!-- 輔助鏈接 -->
<div class="links">
<a href="#" @mouseenter="changeBgColor('#a1c4fd')">忘記密碼?</a>
<a href="#" @mouseenter="changeBgColor('#fbc2eb')">註冊賬號</a>
</div>
</form>
</div>
</div>
</tem)plate>
💡 説明:
- •
@mousemove="handleMouseMove"實現背景隨鼠標移動而旋轉漸變。 - •
floating-icons是背景中漂浮的圓形光點,通過v-for渲染多個。 - • 表單提交使用
@submit.prevent阻止默認刷新行為。
二、邏輯實現(Script 部分)
<script setup>
import { ref, onMounted } from 'vue';
// 用户輸入數據
const username = ref('');
const password = ref('');
// 浮動圖標數據
const icons = ref([]);
// 生成浮動圖標
const createIcons = () => {
const iconsCount = 10;
const newIcons = [];
for (let i = 0; i < iconsCount; i++) {
newIcons.push({
left: Math.random() * 380, // 隨機水平位置
top: Math.random() * 400, // 隨機起始高度
size: 20 + Math.random() * 30 // 隨機大小(20~50px)
});
}
icons.value = newIcons;
};
// 表單提交
const handleSubmit = () => {
alert(`歡迎, ${username.value}!`);
};
// 鼠標懸停鏈接時改變背景色
const changeBgColor = (color) => {
document.body.style.background = `linear-gradient(45deg, ${color}, ${color.replace(')', '')}80)`;
};
// 鼠標移動時動態改變背景角度
const handleMouseMove = (e) => {
const x = e.clientX / window.innerWidth; // 0 ~ 1
const angle = x * 180; // 0 ~ 180deg
document.body.style.background = `linear-gradient(${angle}deg, #ff9a9e, #fad0c4)`;
};
// 組件掛載後初始化圖標
onMounted(() => {
createIcons();
});
</script>
🔍 關鍵點解析:
- •
createIcons()在頁面加載時生成 10 個隨機位置和大小的“光點”。 - •
handleMouseMove根據鼠標 X 座標控制漸變角度,實現“視角跟隨”效果。 - •
changeBgColor在鼠標進入鏈接時切換背景主題色,增強交互反饋。
三、樣式設計(Style 部分)
1. 全局重置與字體
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
2. 頁面容器 .login-page
.login-page {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(45deg, #ff9a9e, #fad0c4);
transition: background 0.5s ease;
}
使用flex居中,背景為粉色系漸變,支持過渡動畫。
3. 登錄框 .login-container
.login-container {
position: relative;
width: 380px;
padding: 40px;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px); /* 毛玻璃效果 */
border-radius: 20px;
box-shadow: 0 25px 45px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.login-container:hover {
transform: translateY(-5px);
box-shadow: 0 30px 50px rgba(0, 0, 0, 0.15);
}
backdrop-filter: blur(10px) 實現毛玻璃質感,極具現代感!
4. 懸停高光掠過動畫
.login-container::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
transition: 0.5s;
}
.login-container:hover::before {
left: 100%;
}
當鼠標懸停時,一束高光從左向右掃過登錄框,視覺衝擊力強。
5. 輸入框動效
.input-group input {
width: 100%;
padding: 15px 20px;
background: rgba(255, 255, 255, 0.2);
border: none;
outline: none;
border-radius: 35px;
color: #fff;
font-size: 16px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.input-group input:focus, .input-group input:hover {
background: rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
}
.input-group label {
position: absolute;
top: 15px;
left: 20px;
color: rgba(255, 255, 255, 0.8);
pointer-events: none;
transition: all 0.3s ease;
}
.input-group input:focus + label,
.input-group input:valid + label {
top: -10px;
left: 15px;
font-size: 12px;
background: rgba(255, 255, 255, 0.2);
padding: 0 10px;
border-radius: 10px;
}
實現 Material Design 風格的標籤上滑動效,用户體驗極佳。
6. 登錄按鈕動效
.btn {
width: 100%;
padding: 15px;
background: linear-gradient(45deg, #ff9a9e, #fad0c4);
border: none;
border-radius: 35px;
color: #fff;
font-weight: 600;
cursor: pointer;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
}
.btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
transition: 0.5s;
}
.btn:hover::before {
left: 100%;
}
懸停時按鈕上浮 + 高光掃過,交互感滿分!
7. 浮動光點動畫
.floating-icons {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: -1;
}
.floating-icons span {
position: absolute;
display: block;
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
animation: float 15s linear infinite;
opacity: 0;
}
.login-container:hover .floating-icons span {
opacity: 1;
}
@keyframes float {
0% {
transform: translateY(0) rotate(0deg);
opacity: 1;
}
100% {
transform: translateY(-1000px) rotate(720deg);
opacity: 0;
}
}
✨ 光點在登錄框懸停時浮現,並向上漂浮、旋轉、淡出,營造夢幻氛圍。
四、完整代碼
<template>
<div class="login-page" @mousemove="handleMouseMove">
<div class="login-container">
<div class="floating-icons">
<span
v-for="(icon, index) in icons"
:key="index"
:style="{
left: icon.left + 'px',
transform: translateY( icon.top + 'px',
width: icon.size + 'px',
height: icon.size + 'px',
animationDuration: (10 + Math.random() * 20) + 's',
animationDelay: (Math.random() * 5) + 's'
}"
></span>
</div>
<h2>歡迎登錄</h2>
<form @submit.prevent="handleSubmit">
<div class="input-group">
<input type="text" required v-model="username">
<label>用户名</label>
</div>
<div class="input-group">
<input type="password" required v-model="password">
<label>密碼</label>
</div>
<button type="submit" class="btn">登 錄</button>
<div class="links">
<a href="#" @mouseenter="changeBgColor('#a1c4fd')">忘記密碼?</a>
<a href="#" @mouseenter="changeBgColor('#fbc2eb')">註冊賬號</a>
</div>
</form>
</div>
</div>
</tem)plate>
<script setup>
import { ref, onMounted } from 'vue';
const username = ref('');
const password = ref('');
const icons = ref([]);
const createIcons = () => {
const iconsCount = 10;
const newIcons = [];
for (let i = 0; i < iconsCount; i++) {
newIcons.push({
left: Math.random() * 380,
top: Math.random() * 400,
size: 20 + Math.random() * 30
});
}
icons.value = newIcons;
};
const handleSubmit = () => {
alert(`歡迎, ${username.value}!`);
};
const changeBgColor = (color) => {
document.body.style.background = `linear-gradient(45deg, ${color}, ${color.replace(')', '')}80)`;
};
const handleMouseMove = (e) => {
const x = e.clientX / window.innerWidth;
document.body.style.background = `linear-gradient(${x * 180}deg, #ff9a9e, #fad0c4)`;
};
onMounted(() => {
createIcons();
});
</script>
<style scoped>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.login-page {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(45deg, #ff9a9e, #fad0c4);
transition: background 0.5s ease;
}
.login-container {
position: relative;
width: 380px;
padding: 40px;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
border-radius: 20px;
box-shadow: 0 25px 45px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.login-container:hover {
transform: translateY(-5px);
box-shadow: 0 30px 50px rgba(0, 0, 0, 0.15);
}
.login-container::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
transition: 0.5s;
}
.login-container:hover::before {
left: 100%;
}
h2 {
color: #fff;
text-align: center;
margin-bottom: 30px;
font-size: 2em;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
}
.input-group {
position: relative;
margin-bottom: 30px;
}
.input-group input {
width: 100%;
padding: 15px 20px;
background: rgba(255, 255, 255, 0.2);
border: none;
outline: none;
border-radius: 35px;
color: #fff;
font-size: 16px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.input-group input:focus, .input-group input:hover {
background: rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
}
.input-group label {
position: absolute;
top: 15px;
left: 20px;
color: rgba(255, 255, 255, 0.8);
pointer-events: none;
transition: all 0.3s ease;
}
.input-group input:focus + label,
.input-group input:valid + label {
top: -10px;
left: 15px;
font-size: 12px;
background: rgba(255, 255, 255, 0.2);
padding: 0 10px;
border-radius: 10px;
}
.btn {
position: relative;
width: 100%;
padding: 15px;
background: linear-gradient(45deg, #ff9a9e, #fad0c4);
border: none;
outline: none;
border-radius: 35px;
color: #fff;
font-size: 16px;
font-weight: 600;
cursor: pointer;
overflow: hidden;
transition: all 0.3s ease;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
}
.btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
transition: 0.5s;
}
.btn:hover::before {
left: 100%;
}
.links {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.links a {
color: rgba(255, 255, 255, 0.8);
text-decoration: none;
font-size: 14px;
transition: all 0.3s ease;
}
.links a:hover {
color: #fff;
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
}
.floating-icons {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: -1;
}
.floating-icons span {
position: absolute;
display: block;
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
animation: float 15s linear infinite;
opacity: 0;
transition: opacity 0.5s ease;
}
.login-container:hover .floating-icons span {
opacity: 1;
}
@keyframes float {
0% {
transform: translateY(0) rotate(0deg);
opacity: 1;
}
100% {
transform: translateY(-1000px) rotate(720deg);
opacity: 0;
}
}
</style>
優化建議
- • 性能優化:浮動圖標數量可控制,避免過多影響性能。
- • 響應式:添加
@media查詢適配移動端。 - • 表單驗證:增加正則校驗、非空判斷。
- • 主題切換:封裝背景色切換邏輯為
themeService。 - • 動畫庫:可接入
Animate.css或GSAP增強動效。
總結
這篇文章一步步實現了一個高顏值、強交互的 Vue3 登錄頁,涵蓋了:
- • Vue3 Composition API 使用
- • 動態樣式綁定
- • 鼠標事件監聽
- • CSS3 動畫與過渡
- • 毛玻璃(backdrop-filter)特效
- • 漸變背景與視覺動效
這個登錄頁不僅適合個人項目、後台系統、SaaS 平台,還可以作為你作品集中的亮點之一。