Buckets:

immErfanrajabee/bd / loading-screen.ts
imerfanrajabee's picture
download
raw
10.8 kB
import { LitElement, css, html } from 'lit';
import { customElement, state } from 'lit/decorators.js';
@customElement('gdm-loading-screen')
export class GDMLoadingScreen extends LitElement {
@state() private progress = 0;
@state() private statusText = '';
@state() private visible = true;
@state() private phase: 'intro' | 'loading' | 'ready' | 'exit' = 'intro';
private resolveReady!: () => void;
ready: Promise<void> = new Promise((r) => (this.resolveReady = r));
private statuses = [
{ at: 0, text: 'بارگذاری ماژول‌های سه‌بعدی...', dur: 600 },
{ at: 18, text: 'اتصال به موتور هوش مصنوعی...', dur: 800 },
{ at: 35, text: 'راه‌اندازی سیستم صوتی...', dur: 700 },
{ at: 50, text: 'کالیبره کردن پردازشگر جیوه...', dur: 600 },
{ at: 62, text: 'فعال‌سازی شبکه عصبی...', dur: 900 },
{ at: 76, text: 'آماده‌سازی محیط تعاملی...', dur: 700 },
{ at: 88, text: 'نهایت‌سازی ارتباط...', dur: 500 },
{ at: 100, text: 'آماده!', dur: 400 },
];
private statusIdx = 0;
private animFrame = 0;
private particles: { x: number; y: number; vx: number; vy: number; r: number; a: number; s: number }[] = [];
private canvas!: HTMLCanvasElement;
private ctx!: CanvasRenderingContext2D;
static styles = css`
:host {
position: fixed; inset: 0; z-index: 9999;
display: flex; align-items: center; justify-content: center;
direction: rtl;
font-family: 'Vazirmatn', 'Tahoma', sans-serif;
overflow: hidden;
transition: opacity 0.8s ease, transform 0.8s ease;
}
:host(.hidden) {
opacity: 0;
transform: scale(1.05);
pointer-events: none;
}
canvas {
position: absolute; inset: 0;
width: 100%; height: 100%;
}
.content {
position: relative; z-index: 2;
display: flex; flex-direction: column;
align-items: center; gap: 32px;
}
.orb-container {
position: relative;
width: 120px; height: 120px;
display: flex; align-items: center; justify-content: center;
}
.orb {
width: 80px; height: 80px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%, #e8f0ff, #7090c0 50%, #304060 100%);
box-shadow:
0 0 60px rgba(100, 160, 255, 0.3),
0 0 120px rgba(100, 160, 255, 0.15),
inset 0 -10px 40px rgba(0, 0, 0, 0.4);
animation: orbPulse 3s ease-in-out infinite;
position: relative;
}
.orb::before {
content: '';
position: absolute; inset: 2px;
border-radius: 50%;
background: radial-gradient(circle at 40% 35%, rgba(255, 255, 255, 0.5) 0%, transparent 60%);
}
.orb-ring {
position: absolute;
border-radius: 50%;
border: 1.5px solid rgba(120, 180, 255, 0.2);
animation: ringExpand 3s ease-out infinite;
}
.orb-ring:nth-child(2) { width: 100px; height: 100px; animation-delay: 0.5s; }
.orb-ring:nth-child(3) { width: 120px; height: 120px; animation-delay: 1s; border-color: rgba(120, 180, 255, 0.1); }
@keyframes ringExpand {
0% { transform: scale(0.8); opacity: 0.8; }
100% { transform: scale(1.5); opacity: 0; }
}
@keyframes orbPulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.03); }
}
.title {
font-size: 22px;
font-weight: 300;
letter-spacing: 3px;
color: rgba(255, 255, 255, 0.9);
text-shadow: 0 2px 20px rgba(100, 160, 255, 0.2);
display: flex; flex-direction: column; align-items: center; gap: 6px;
}
.title-fa {
font-size: 26px;
font-weight: 600;
background: linear-gradient(135deg, #c8d8f0, #88bbff, #aaccff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shimmer 3s ease-in-out infinite;
}
.title-en {
font-size: 11px;
font-weight: 400;
color: rgba(255, 255, 255, 0.25);
letter-spacing: 5px;
text-transform: uppercase;
}
@keyframes shimmer {
0%, 100% { filter: brightness(1); }
50% { filter: brightness(1.3); }
}
.progress-wrap {
width: 240px;
position: relative;
}
.progress-track {
width: 100%; height: 2px;
background: rgba(255, 255, 255, 0.06);
border-radius: 2px;
overflow: hidden;
position: relative;
}
.progress-fill {
height: 100%;
width: 0%;
background: linear-gradient(90deg, #4488ff, #88ccff, #aaccff);
border-radius: 2px;
transition: width 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow: 0 0 12px rgba(100, 160, 255, 0.4);
position: relative;
}
.progress-fill::after {
content: '';
position: absolute; top: 0; right: 0; bottom: 0;
width: 30px;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent);
animation: glide 1.5s ease-in-out infinite;
}
@keyframes glide {
0% { right: 100%; }
100% { right: -30px; }
}
.status {
font-size: 12px;
color: rgba(255, 255, 255, 0.35);
text-align: center;
min-height: 20px;
transition: all 0.3s ease;
letter-spacing: 1px;
}
.ready-btn {
margin-top: 8px;
padding: 12px 40px;
border-radius: 30px;
border: 1px solid rgba(100, 160, 255, 0.3);
background: rgba(100, 160, 255, 0.1);
color: #aaccff;
font-size: 14px;
font-family: inherit;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
backdrop-filter: blur(10px);
letter-spacing: 1px;
opacity: 0;
transform: translateY(10px);
animation: fadeUp 0.6s ease forwards;
}
.ready-btn:hover {
background: rgba(100, 160, 255, 0.2);
border-color: rgba(100, 160, 255, 0.6);
box-shadow: 0 8px 30px rgba(100, 160, 255, 0.15);
transform: translateY(-2px) scale(1.02);
}
@keyframes fadeUp {
to { opacity: 1; transform: translateY(0); }
}
.percent {
font-size: 11px;
color: rgba(255, 255, 255, 0.2);
text-align: center;
margin-top: 4px;
font-variant-numeric: tabular-nums;
}
`;
constructor() {
super();
this.statusText = this.statuses[0].text;
}
connectedCallback() {
super.connectedCallback();
setTimeout(() => this.startParticles(), 50);
setTimeout(() => this.startLoading(), 400);
}
disconnectedCallback() {
super.disconnectedCallback();
cancelAnimationFrame(this.animFrame);
}
private startParticles() {
this.canvas = this.shadowRoot!.querySelector('canvas')!;
this.ctx = this.canvas.getContext('2d')!;
this.resize();
window.addEventListener('resize', () => this.resize());
for (let i = 0; i < 60; i++) this.particles.push(this.createParticle());
this.renderParticles();
}
private resize() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
}
private createParticle() {
return {
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
vx: (Math.random() - 0.5) * 0.3,
vy: (Math.random() - 0.5) * 0.3 - 0.1,
r: 0.5 + Math.random() * 1.5,
a: 0.1 + Math.random() * 0.3,
s: Math.random() * 0.02 + 0.005,
};
}
private renderParticles = () => {
this.animFrame = requestAnimationFrame(this.renderParticles);
const ctx = this.ctx;
const w = this.canvas.width;
const h = this.canvas.height;
ctx.clearRect(0, 0, w, h);
for (const p of this.particles) {
p.x += p.vx;
p.y += p.vy;
p.a += (Math.sin(Date.now() * p.s) * 0.5 + 0.5 - p.a) * 0.02;
if (p.x < -10) p.x = w + 10;
if (p.x > w + 10) p.x = -10;
if (p.y < -10) p.y = h + 10;
if (p.y > h + 10) p.y = -10;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fillStyle = `rgba(150, 200, 255, ${p.a * 0.4})`;
ctx.fill();
}
// Connections
for (let i = 0; i < this.particles.length; i++) {
for (let j = i + 1; j < this.particles.length; j++) {
const a = this.particles[i];
const b = this.particles[j];
const dx = a.x - b.x;
const dy = a.y - b.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 120) {
const alpha = (1 - dist / 120) * 0.08;
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.strokeStyle = `rgba(100, 160, 255, ${alpha})`;
ctx.lineWidth = 0.5;
ctx.stroke();
}
}
}
};
private startLoading() {
this.phase = 'loading';
this.advanceProgress();
}
private advanceProgress = () => {
if (this.statusIdx >= this.statuses.length) return;
const status = this.statuses[this.statusIdx];
if (this.progress < status.at) {
this.progress += 0.5 + Math.random() * 1.5;
if (this.progress > status.at) this.progress = status.at;
setTimeout(this.advanceProgress, 30 + Math.random() * 60);
return;
}
this.statusText = status.text;
this.statusIdx++;
if (this.statusIdx >= this.statuses.length) {
this.progress = 100;
setTimeout(() => this.onReady(), 500);
return;
}
setTimeout(this.advanceProgress, status.dur);
};
private onReady() {
this.phase = 'ready';
this.resolveReady();
}
dismiss() {
this.classList.add('hidden');
this.dispatchEvent(new CustomEvent('start-request', { bubbles: true, composed: true }));
setTimeout(() => {
this.visible = false;
this.remove();
}, 800);
}
render() {
return html`
<canvas></canvas>
<div class="content">
<div class="orb-container">
<div class="orb-ring"></div>
<div class="orb-ring"></div>
<div class="orb"></div>
</div>
<div class="title">
<span class="title-fa">دستیار هوشمند</span>
<span class="title-en">Persian AI Assistant</span>
</div>
<div class="progress-wrap">
<div class="progress-track">
<div class="progress-fill" style="width:${this.progress}%"></div>
</div>
<div class="percent">${Math.round(this.progress)}%</div>
</div>
<div class="status">${this.statusText}</div>
${this.phase === 'ready'
? html`<button class="ready-btn" @click=${this.dismiss}>شروع گفتگو</button>`
: ''}
</div>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'gdm-loading-screen': GDMLoadingScreen;
}
}

Xet Storage Details

Size:
10.8 kB
·
Xet hash:
8e81f1bd6e59208f5d6463e16448ef8b77dd7055d690152734569582bc320380

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.