大家好,我是大華!在現代 Web 開發中,使用者體驗越來越重要。一個簡潔、美觀又富有動效的登入頁,不僅能提升品牌形象,還能增強使用者的第一印象。這篇文章,我們將用 Vue3 + CSS3 動畫,來實現一個懸浮動畫和細膩互動效果的 Vue3 登入頁面。
項目效果預覽:
<script setup>
語法)ref
, onMounted
)<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',
top: 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>
</template>
💡 說明:
@mousemove="handleMouseMove"
實現背景隨鼠標移動而旋轉漸變。floating-icons
是背景中漂浮的圓形光點,通過v-for
渲染多個。@submit.prevent
阻止默認刷新行為。<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
在滑鼠進入連結時切換背景主題色,加強互動反饋。* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.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
居中,背景為粉色系漸變,支持過渡動畫。
.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)
實現毛玻璃質感,極具現代感!
.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%;
}
當滑鼠懸停時,一束高光從左向右掃過登入框,視覺衝擊力強。
.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;
}
實現 Material Design 風格的標籤上滑動效,使用者體驗極佳。
.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%;
}
懸停時按鈕上浮 + 高光掃過,互動感滿分!
.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',
top: 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>
</template>
<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;
const angle = x * 180;
document.body.style.background = `linear-gradient(${angle}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%;
}
.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 {
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%;
}
.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;
}
}
</style>
@media
查詢適配移動端。themeService
。Animate.css
或GSAP
增強動效。這篇文章一步步實現了一個高顏值、強互動的 Vue3 登入頁,涵蓋了:
這個登入頁不僅適合個人項目、後台系統、SaaS 平台,還可以作為你作品集中的亮點之一。
本文首發於公眾號:程序員劉大華,專注分享前後端開發的實戰筆記。關注我,少走彎路,一起進步!
《SpringBoot 中的 7 種耗時統計方式,你用過幾種?》
《千萬級大表如何新增字段?別再直接 ALTER 了》
《Vue3 如何優雅地實現一個全局的 loading 組件》
《我用 Vue3 + Canvas 做了個超實用的水印工具,同事都在搶著用》