mobile . themes
This commit is contained in:
@@ -2,15 +2,16 @@
|
||||
{% block title %}登录 - 智联青云{% endblock %}
|
||||
{% block navbar %}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="min-h-screen bg-slate-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<canvas id="meteor-canvas" style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:0;pointer-events:none;"></canvas>
|
||||
<div class="min-h-screen flex flex-col justify-center py-12 sm:px-6 lg:px-8 relative" style="z-index:1;">
|
||||
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<h2 class="mt-6 text-center text-3xl font-extrabold bg-gradient-to-r from-blue-600 via-purple-600 to-pink-600 bg-clip-text text-transparent animate-pulse">登录您的账户</h2>
|
||||
<p class="mt-2 text-center text-sm text-slate-600">
|
||||
<p class="mt-2 text-center text-sm text-slate-300">
|
||||
或者 <a href="/register" class="font-medium text-primary hover:text-blue-500 transition-all duration-300 hover:scale-110 inline-block">注册新账户</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div class="futuristic-card py-8 px-4 sm:px-10">
|
||||
<div class="futuristic-card py-8 px-4 sm:px-10" style="background:rgba(255,255,255,0.92);backdrop-filter:blur(12px);">
|
||||
<div class="flex border-b border-slate-200 mb-6">
|
||||
<button id="tab-phone" onclick="switchTab('phone')" class="flex-1 pb-4 text-sm font-medium text-center text-primary border-b-2 border-primary transition-all duration-300 hover:scale-105">手机验证码登录</button>
|
||||
<button id="tab-email" onclick="switchTab('email')" class="flex-1 pb-4 text-sm font-medium text-center text-slate-500 hover:text-slate-700 transition-all duration-300 hover:scale-105">邮箱密码登录</button>
|
||||
@@ -143,5 +144,109 @@ async function handleEmailLogin(e) {
|
||||
}
|
||||
|
||||
fetchCaptcha('phone');
|
||||
|
||||
// 流星粒子背景
|
||||
(function() {
|
||||
const canvas = document.getElementById('meteor-canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
let W, H;
|
||||
window._mouseX = -9999;
|
||||
window._mouseY = -9999;
|
||||
document.addEventListener('mousemove', function(e) { window._mouseX = e.clientX; window._mouseY = e.clientY; });
|
||||
document.addEventListener('mouseleave', function() { window._mouseX = -9999; window._mouseY = -9999; });
|
||||
|
||||
function resize() {
|
||||
W = canvas.width = window.innerWidth;
|
||||
H = canvas.height = window.innerHeight;
|
||||
}
|
||||
resize();
|
||||
window.addEventListener('resize', resize);
|
||||
|
||||
const COUNT = 120;
|
||||
const MOUSE_R = 150;
|
||||
const REPEL = 8;
|
||||
const particles = [];
|
||||
|
||||
function rand(a, b) { return Math.random() * (b - a) + a; }
|
||||
|
||||
function spawn(warm) {
|
||||
const angle = rand(Math.PI * 0.6, Math.PI * 0.8);
|
||||
const speed = rand(2, 5);
|
||||
const p = {
|
||||
x: rand(0, W * 1.2),
|
||||
y: warm ? rand(-H * 0.3, H) : rand(-H * 0.3, 0),
|
||||
vx: Math.cos(angle) * speed,
|
||||
vy: Math.sin(angle) * speed,
|
||||
len: rand(15, 40),
|
||||
size: rand(0.8, 2),
|
||||
alpha: rand(0.3, 0.9),
|
||||
hue: rand(190, 260),
|
||||
life: 0,
|
||||
maxLife: rand(120, 300)
|
||||
};
|
||||
if (warm) p.life = rand(0, p.maxLife);
|
||||
return p;
|
||||
}
|
||||
|
||||
for (let i = 0; i < COUNT; i++) particles.push(spawn(true));
|
||||
|
||||
function loop() {
|
||||
ctx.clearRect(0, 0, W, H);
|
||||
|
||||
const bg = ctx.createRadialGradient(W/2, H/2, 0, W/2, H/2, Math.max(W, H) * 0.7);
|
||||
bg.addColorStop(0, '#0f172a');
|
||||
bg.addColorStop(1, '#020617');
|
||||
ctx.fillStyle = bg;
|
||||
ctx.fillRect(0, 0, W, H);
|
||||
|
||||
const mx = window._mouseX, my = window._mouseY;
|
||||
|
||||
for (let i = 0; i < particles.length; i++) {
|
||||
const p = particles[i];
|
||||
p.life++;
|
||||
|
||||
const dx = p.x - mx, dy = p.y - my;
|
||||
const dist = Math.sqrt(dx * dx + dy * dy);
|
||||
if (dist < MOUSE_R && dist > 0) {
|
||||
const f = (1 - dist / MOUSE_R) * REPEL;
|
||||
p.x += (dx / dist) * f;
|
||||
p.y += (dy / dist) * f;
|
||||
}
|
||||
|
||||
p.x += p.vx;
|
||||
p.y += p.vy;
|
||||
|
||||
let a = p.alpha;
|
||||
if (p.life < 20) a *= p.life / 20;
|
||||
if (p.life > p.maxLife - 30) a *= (p.maxLife - p.life) / 30;
|
||||
|
||||
const spd = Math.sqrt(p.vx * p.vx + p.vy * p.vy);
|
||||
const tx = p.x - (p.vx / spd) * p.len * 0.6;
|
||||
const ty = p.y - (p.vy / spd) * p.len * 0.6;
|
||||
|
||||
const g = ctx.createLinearGradient(tx, ty, p.x, p.y);
|
||||
g.addColorStop(0, `hsla(${p.hue},80%,70%,0)`);
|
||||
g.addColorStop(1, `hsla(${p.hue},80%,70%,${a})`);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(tx, ty);
|
||||
ctx.lineTo(p.x, p.y);
|
||||
ctx.strokeStyle = g;
|
||||
ctx.lineWidth = p.size;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(p.x, p.y, p.size * 0.8, 0, Math.PI * 2);
|
||||
ctx.fillStyle = `hsla(${p.hue},90%,85%,${a})`;
|
||||
ctx.fill();
|
||||
|
||||
if (p.life >= p.maxLife || p.y > H + 50 || p.x < -100) {
|
||||
particles[i] = spawn(false);
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(loop);
|
||||
}
|
||||
loop();
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user