feat(dashboard): add animated diamond background to login page

This commit is contained in:
LIghtJUNction
2026-03-29 16:11:58 +08:00
parent 0ee84ae362
commit d13ff84a20
2 changed files with 141 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
<template>
<div class="diamond-bg">
<canvas ref="canvas"></canvas>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from "vue";
const canvas = ref<HTMLCanvasElement | null>(null);
let animationId: number | null = null;
interface Diamond {
x: number;
y: number;
size: number;
phase: number;
speed: number;
}
const createDiamonds = (w: number, h: number): Diamond[] => {
const diamonds: Diamond[] = [];
const spacing = 48;
const cols = Math.ceil(w / spacing) + 2;
const rows = Math.ceil(h / spacing) + 2;
for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) {
const offset = (j % 2) * (spacing / 2);
diamonds.push({
x: i * spacing + offset,
y: j * spacing * 0.75,
size: 8 + Math.random() * 6,
phase: (i + j * 0.5) * 0.8,
speed: 0.4 + Math.random() * 0.3,
});
}
}
return diamonds;
};
const drawDiamond = (
ctx: CanvasRenderingContext2D,
x: number,
y: number,
size: number,
alpha: number,
raised: boolean
) => {
const d = size / 2;
const depth = raised ? 0 : size * 0.25;
// Main body
ctx.beginPath();
ctx.moveTo(x, y - d + depth);
ctx.lineTo(x + d + depth, y);
ctx.lineTo(x, y + d - depth);
ctx.lineTo(x - d - depth, y);
ctx.closePath();
// Fill with subtle gradient
const grad = ctx.createLinearGradient(x - d, y - d, x + d, y + d);
if (raised) {
grad.addColorStop(0, `rgba(147, 197, 253, ${alpha})`);
grad.addColorStop(1, `rgba(59, 130, 246, ${alpha * 0.8})`);
} else {
grad.addColorStop(0, `rgba(30, 64, 175, ${alpha * 0.6})`);
grad.addColorStop(1, `rgba(30, 58, 138, ${alpha * 0.4})`);
}
ctx.fillStyle = grad;
ctx.fill();
// Top edge highlight
ctx.strokeStyle = `rgba(255, 255, 255, ${alpha * (raised ? 0.5 : 0.2)})`;
ctx.lineWidth = 0.8;
ctx.beginPath();
ctx.moveTo(x - d + depth, y);
ctx.lineTo(x, y - d + depth);
ctx.lineTo(x + d + depth, y);
ctx.stroke();
};
onMounted(() => {
if (!canvas.value) return;
const ctx = canvas.value.getContext("2d");
if (!ctx) return;
let diamonds: Diamond[] = [];
let t = 0;
const resize = () => {
if (!canvas.value) return;
canvas.value.width = window.innerWidth;
canvas.value.height = window.innerHeight;
diamonds = createDiamonds(canvas.value.width, canvas.value.height);
};
const animate = () => {
if (!canvas.value || !ctx) return;
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
t += 0.015;
diamonds.forEach((d) => {
const floatY = Math.sin(t * d.speed + d.phase) * 5;
const raised = Math.sin(t * d.speed + d.phase) > 0;
const alpha = 0.25 + Math.abs(Math.sin(t * d.speed + d.phase)) * 0.35;
drawDiamond(ctx, d.x, d.y + floatY, d.size, alpha, raised);
});
animationId = requestAnimationFrame(animate);
};
resize();
animate();
window.addEventListener("resize", resize);
});
onUnmounted(() => {
if (animationId !== null) cancelAnimationFrame(animationId);
});
</script>
<style scoped>
.diamond-bg {
position: fixed;
inset: 0;
z-index: 0;
background: linear-gradient(145deg, #0c1322 0%, #1a2744 40%, #0c1322 100%);
}
canvas {
display: block;
width: 100%;
height: 100%;
}
</style>

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import AuthLogin from "../authForms/AuthLogin.vue";
import DailyQuote from "@/components/shared/DailyQuote.vue";
import DiamondBg from "@/components/auth/DiamondBg.vue";
import LanguageSwitcher from "@/components/shared/LanguageSwitcher.vue";
import { onMounted, ref } from "vue";
import { useAuthStore } from "@/stores/auth";
@@ -113,6 +114,7 @@ onMounted(() => {
<template>
<div class="login-page-container">
<DiamondBg />
<v-card class="login-card" elevation="1">
<v-card-title>
<div class="d-flex justify-space-between align-center w-100">
@@ -314,5 +316,8 @@ onMounted(() => {
.login-card {
width: 400px;
padding: 8px;
background: rgba(30, 41, 59, 0.85) !important;
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
</style>