Files
Montech_Landing/apps/landing/components/IntroLoader.vue
T

95 lines
2.9 KiB
Vue

<template>
<Transition name="intro-fade">
<div
v-if="visible"
class="fixed inset-0 z-[1000] flex flex-col items-center justify-center overflow-hidden bg-ink text-bone"
aria-live="polite"
aria-busy="true"
>
<div class="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_at_center,rgba(200,255,46,0.12),transparent_55%)]" />
<p
class="relative px-6 text-center font-display font-extrabold leading-[0.95] tracking-[-0.04em]"
:style="{ fontSize: 'clamp(2.6rem, 13vw, 9rem)' }"
>
<span class="block animate-fade-up">{{ brandParts.first }}</span>
<span
v-if="brandParts.second"
class="mt-2 block font-accent tracking-normal text-volt animate-fade-up md:mt-3"
style="animation-delay: 0.12s"
>{{ brandParts.second }}</span>
</p>
<div class="relative mt-16 flex w-[min(300px,72vw)] flex-col items-center gap-3 animate-fade-up" style="animation-delay: 0.25s">
<div class="h-px w-full overflow-hidden bg-white/10">
<div class="h-full bg-volt transition-[width] duration-75 ease-linear" :style="{ width: `${progress}%` }" />
</div>
<span class="font-mono text-xs tracking-[0.4em] text-ash tabular-nums" dir="ltr">
{{ String(Math.floor(progress)).padStart(2, "0") }}%
</span>
</div>
</div>
</Transition>
</template>
<script setup lang="ts">
const props = withDefaults(
defineProps<{ title?: string }>(),
{ title: "هوش‌افزار منتک" },
);
const visible = ref(false);
const progress = ref(0);
const brandParts = computed(() => {
const raw = (props.title || "هوش‌افزار منتک").trim();
const parts = raw.split(/\s+/);
if (parts.length <= 1) return { first: raw, second: "" };
return {
first: parts.slice(0, -1).join(" "),
second: parts.at(-1) || "",
};
});
onMounted(() => {
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduced) return;
visible.value = true;
document.documentElement.style.overflow = "hidden";
const start = performance.now();
const duration = 2400;
let raf = 0;
const tick = (now: number) => {
const t = Math.min(1, (now - start) / duration);
const eased = 1 - Math.pow(1 - t, 3);
progress.value = Math.min(100, eased * 100);
if (t < 1) {
raf = requestAnimationFrame(tick);
return;
}
progress.value = 100;
window.setTimeout(() => {
visible.value = false;
document.documentElement.style.overflow = "";
}, 320);
};
raf = requestAnimationFrame(tick);
onUnmounted(() => {
cancelAnimationFrame(raf);
document.documentElement.style.overflow = "";
});
});
</script>
<style scoped>
.intro-fade-leave-active {
transition: opacity 0.75s cubic-bezier(0.22, 1, 0.36, 1), transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);
}
.intro-fade-leave-to {
opacity: 0;
transform: scale(1.06);
}
</style>