big ui refractor
This commit is contained in:
@@ -1,143 +1,171 @@
|
||||
<template lang="pug">
|
||||
v-app.zen-app
|
||||
v-navigation-drawer(
|
||||
v-model="drawer"
|
||||
temporary
|
||||
location="right"
|
||||
color="#1e1e24"
|
||||
class="border-none"
|
||||
width="280"
|
||||
v-app.zen-app.overflow-hidden
|
||||
.parallax-bg(
|
||||
:style="parallaxStyle"
|
||||
:class="{ 'zooming': viewState === 'zooming' }"
|
||||
)
|
||||
.d-flex.flex-column.h-100.pa-4
|
||||
.d-flex.align-center.mb-6.px-2
|
||||
img.mr-3(:src="logo" height="32" width="32" alt="Logo")
|
||||
span.text-h6.font-weight-bold {{ $t('nav.menu') }}
|
||||
.bg-gradient
|
||||
.bg-grid
|
||||
|
||||
v-list.pa-0(nav bg-color="transparent")
|
||||
v-list-item.mb-2(
|
||||
to="/"
|
||||
rounded="lg"
|
||||
:active="$route.path === '/'"
|
||||
)
|
||||
template(v-slot:prepend)
|
||||
v-icon(color="#00cec9" icon="mdi-view-dashboard")
|
||||
v-list-item-title.font-weight-bold {{ $t('nav.dashboard') }}
|
||||
.kanji-layer
|
||||
span.bg-kanji(
|
||||
v-for="(k, i) in backgroundKanjis"
|
||||
:key="i"
|
||||
:style="k.style"
|
||||
) {{ k.char }}
|
||||
|
||||
v-list-item.mb-2(
|
||||
to="/collection"
|
||||
rounded="lg"
|
||||
:active="$route.path === '/collection'"
|
||||
)
|
||||
template(v-slot:prepend)
|
||||
v-icon(color="#00cec9" icon="mdi-bookshelf")
|
||||
v-list-item-title.font-weight-bold {{ $t('nav.collection') }}
|
||||
.orb.orb-1(:class="{ 'zooming': viewState === 'zooming' }")
|
||||
.orb.orb-2(:class="{ 'zooming': viewState === 'zooming' }")
|
||||
.orb.orb-3(:class="{ 'zooming': viewState === 'zooming' }")
|
||||
|
||||
v-divider.my-4.border-subtle
|
||||
|
||||
.d-flex.flex-column.gap-2
|
||||
v-btn.justify-start.px-4(
|
||||
variant="text"
|
||||
block
|
||||
color="grey-lighten-1"
|
||||
@click="showSettings = true; drawer = false"
|
||||
)
|
||||
v-icon(start icon="mdi-cog")
|
||||
| {{ $t('nav.settings') }}
|
||||
|
||||
v-btn.justify-start.px-4(
|
||||
variant="text"
|
||||
block
|
||||
color="grey-lighten-1"
|
||||
:loading="syncing"
|
||||
@click="manualSync"
|
||||
)
|
||||
v-icon(start icon="mdi-sync")
|
||||
| {{ $t('nav.sync') }}
|
||||
|
||||
v-btn.justify-start.px-4.text-red-lighten-2(
|
||||
variant="text"
|
||||
block
|
||||
@click="handleLogout"
|
||||
)
|
||||
v-icon(start icon="mdi-logout")
|
||||
| {{ $t('nav.logout') }}
|
||||
|
||||
v-app-bar.px-2.app-bar-blur.safe-area-header(
|
||||
flat
|
||||
color="rgba(30, 30, 36, 0.8)"
|
||||
border="b"
|
||||
.app-content-wrapper(
|
||||
v-if="store.token || viewState === 'zooming'"
|
||||
:class="{ 'app-entering': viewState === 'zooming', 'app-visible': viewState === 'app' }"
|
||||
)
|
||||
v-app-bar-title.font-weight-bold.text-h6(style="min-width: fit-content;")
|
||||
.d-flex.align-center.cursor-pointer.logo-hover(@click="$router.push('/')")
|
||||
img(:src="logo" height="32" width="32" alt="Zen Kanji Logo")
|
||||
span.ml-3.tracking-tight Zen Kanji
|
||||
v-navigation-drawer(
|
||||
v-model="drawer"
|
||||
temporary
|
||||
location="right"
|
||||
color="#1e1e24"
|
||||
class="border-none"
|
||||
width="280"
|
||||
)
|
||||
.d-flex.flex-column.h-100.pa-4
|
||||
.d-flex.align-center.mb-6.px-2
|
||||
img.mr-3(:src="logo" height="32" width="32" alt="Logo")
|
||||
span.text-h6.font-weight-bold {{ $t('nav.menu') }}
|
||||
|
||||
v-spacer
|
||||
v-list.pa-0(nav bg-color="transparent")
|
||||
v-list-item.mb-2(
|
||||
to="/"
|
||||
rounded="lg"
|
||||
:active="$route.path === '/'"
|
||||
)
|
||||
template(v-slot:prepend)
|
||||
v-icon(color="#00cec9" icon="mdi-view-dashboard")
|
||||
v-list-item-title.font-weight-bold {{ $t('nav.dashboard') }}
|
||||
|
||||
template(v-if="store.token")
|
||||
.d-none.d-md-flex.align-center
|
||||
v-btn.mx-1(
|
||||
to="/"
|
||||
:active="false"
|
||||
variant="text"
|
||||
:color="$route.path === '/' ? '#00cec9' : 'grey'"
|
||||
) {{ $t('nav.dashboard') }}
|
||||
v-list-item.mb-2(
|
||||
to="/collection"
|
||||
rounded="lg"
|
||||
:active="$route.path === '/collection'"
|
||||
)
|
||||
template(v-slot:prepend)
|
||||
v-icon(color="#00cec9" icon="mdi-bookshelf")
|
||||
v-list-item-title.font-weight-bold {{ $t('nav.collection') }}
|
||||
|
||||
v-btn.mx-1(
|
||||
to="/collection"
|
||||
:active="false"
|
||||
variant="text"
|
||||
:color="$route.path === '/collection' ? '#00cec9' : 'grey'"
|
||||
) {{ $t('nav.collection') }}
|
||||
v-divider.my-4.border-subtle
|
||||
|
||||
v-divider.mx-2.my-auto(vertical length="20" color="grey-darken-2")
|
||||
.d-flex.flex-column.gap-2
|
||||
v-btn.justify-start.px-4(
|
||||
variant="text"
|
||||
block
|
||||
color="grey-lighten-1"
|
||||
@click="showSettings = true; drawer = false"
|
||||
)
|
||||
v-icon(start icon="mdi-cog")
|
||||
| {{ $t('nav.settings') }}
|
||||
|
||||
v-tooltip(:text="$t('nav.settings')" location="bottom")
|
||||
template(v-slot:activator="{ props }")
|
||||
v-btn(
|
||||
v-bind="props"
|
||||
icon="mdi-cog"
|
||||
variant="text"
|
||||
color="grey-lighten-1"
|
||||
@click="showSettings = true"
|
||||
)
|
||||
v-btn.justify-start.px-4(
|
||||
variant="text"
|
||||
block
|
||||
color="grey-lighten-1"
|
||||
:loading="syncing"
|
||||
@click="manualSync"
|
||||
)
|
||||
v-icon(start icon="mdi-sync")
|
||||
| {{ $t('nav.sync') }}
|
||||
|
||||
v-tooltip(:text="$t('nav.sync')" location="bottom")
|
||||
template(v-slot:activator="{ props }")
|
||||
v-btn(
|
||||
v-bind="props"
|
||||
icon="mdi-sync"
|
||||
variant="text"
|
||||
:loading="syncing"
|
||||
color="grey-lighten-1"
|
||||
@click="manualSync"
|
||||
)
|
||||
v-btn.justify-start.px-4.text-red-lighten-2(
|
||||
variant="text"
|
||||
block
|
||||
@click="handleLogout"
|
||||
)
|
||||
v-icon(start icon="mdi-logout")
|
||||
| {{ $t('nav.logout') }}
|
||||
|
||||
v-tooltip(:text="$t('nav.logout')" location="bottom")
|
||||
template(v-slot:activator="{ props }")
|
||||
v-btn(
|
||||
v-bind="props"
|
||||
icon="mdi-logout"
|
||||
variant="text"
|
||||
color="grey-darken-1"
|
||||
@click="handleLogout"
|
||||
)
|
||||
v-app-bar.px-2.app-bar-blur.safe-area-header(
|
||||
flat
|
||||
color="rgba(30, 30, 36, 0.8)"
|
||||
border="b"
|
||||
)
|
||||
v-app-bar-title.font-weight-bold.text-h6(style="min-width: fit-content;")
|
||||
.d-flex.align-center.cursor-pointer.logo-hover(@click="$router.push('/')")
|
||||
img(:src="logo" height="32" width="32" alt="Zen Kanji Logo")
|
||||
span.ml-3.tracking-tight Zen Kanji
|
||||
|
||||
.d-flex.d-md-none
|
||||
v-btn(
|
||||
icon="mdi-menu"
|
||||
variant="text"
|
||||
color="grey-lighten-1"
|
||||
@click="drawer = !drawer"
|
||||
)
|
||||
v-spacer
|
||||
|
||||
v-main
|
||||
.fill-height.d-flex.align-center.justify-center.px-4(v-if="!store.token")
|
||||
v-card.pa-6.rounded-lg.elevation-10.text-center.border-subtle(
|
||||
color="#1e1e24"
|
||||
width="100%"
|
||||
max-width="400"
|
||||
)
|
||||
template(v-if="store.token")
|
||||
.d-none.d-md-flex.align-center
|
||||
v-btn.mx-1(
|
||||
to="/"
|
||||
variant="text"
|
||||
:color="$route.path === '/' ? '#00cec9' : 'grey'"
|
||||
) {{ $t('nav.dashboard') }}
|
||||
|
||||
v-btn.mx-1(
|
||||
to="/collection"
|
||||
variant="text"
|
||||
:color="$route.path === '/collection' ? '#00cec9' : 'grey'"
|
||||
) {{ $t('nav.collection') }}
|
||||
|
||||
v-divider.mx-2.my-auto(vertical length="20" color="grey-darken-2")
|
||||
|
||||
v-tooltip(:text="$t('nav.settings')" location="bottom")
|
||||
template(v-slot:activator="{ props }")
|
||||
v-btn(
|
||||
v-bind="props"
|
||||
icon="mdi-cog"
|
||||
variant="text"
|
||||
color="grey-lighten-1"
|
||||
@click="showSettings = true"
|
||||
)
|
||||
|
||||
v-tooltip(:text="$t('nav.sync')" location="bottom")
|
||||
template(v-slot:activator="{ props }")
|
||||
v-btn(
|
||||
v-bind="props"
|
||||
icon="mdi-sync"
|
||||
variant="text"
|
||||
:loading="syncing"
|
||||
color="grey-lighten-1"
|
||||
@click="manualSync"
|
||||
)
|
||||
|
||||
v-tooltip(:text="$t('nav.logout')" location="bottom")
|
||||
template(v-slot:activator="{ props }")
|
||||
v-btn(
|
||||
v-bind="props"
|
||||
icon="mdi-logout"
|
||||
variant="text"
|
||||
color="grey-darken-1"
|
||||
@click="handleLogout"
|
||||
)
|
||||
|
||||
.d-flex.d-md-none
|
||||
v-btn(
|
||||
icon="mdi-menu"
|
||||
variant="text"
|
||||
color="grey-lighten-1"
|
||||
@click="drawer = !drawer"
|
||||
)
|
||||
|
||||
v-main.fill-height
|
||||
router-view(v-slot="{ Component }")
|
||||
transition(name="page" mode="out-in")
|
||||
component(:is="Component")
|
||||
|
||||
.login-overlay(
|
||||
v-if="!store.token || viewState === 'zooming' || viewState === 'authenticating'"
|
||||
:class="{ 'zooming': viewState === 'zooming' }"
|
||||
)
|
||||
v-card.login-card.pa-6.rounded-lg.elevation-10.text-center.border-subtle(
|
||||
color="#1e1e24"
|
||||
width="100%"
|
||||
max-width="400"
|
||||
)
|
||||
.login-content
|
||||
img.mb-4(:src="logo" height="64" width="64")
|
||||
|
||||
h1.text-h5.font-weight-bold.mb-2 {{ $t('hero.welcome') }}
|
||||
@@ -151,7 +179,8 @@
|
||||
color="white"
|
||||
hide-details
|
||||
density="comfortable"
|
||||
@keyup.enter="handleLogin"
|
||||
@keyup.enter="triggerLogin"
|
||||
:disabled="viewState === 'authenticating' || viewState === 'zooming'"
|
||||
)
|
||||
|
||||
v-alert.mb-4.text-left.text-caption(
|
||||
@@ -161,16 +190,14 @@
|
||||
variant="tonal"
|
||||
) {{ errorMsg }}
|
||||
|
||||
v-btn.text-black.font-weight-bold.mt-4(
|
||||
v-btn.text-black.font-weight-bold.mt-4.login-btn(
|
||||
block
|
||||
color="#00cec9"
|
||||
height="44"
|
||||
:loading="loggingIn"
|
||||
@click="handleLogin"
|
||||
:loading="viewState === 'authenticating'"
|
||||
@click="triggerLogin"
|
||||
) {{ $t('login.button') }}
|
||||
|
||||
router-view(v-else)
|
||||
|
||||
v-dialog(v-model="showSettings" max-width="340")
|
||||
v-card.rounded-xl.pa-4.border-subtle(color="#1e1e24")
|
||||
v-card-title.text-center.font-weight-bold {{ $t('settings.title') }}
|
||||
@@ -205,15 +232,15 @@
|
||||
span {{ $t('settings.loose') }} (20)
|
||||
|
||||
.text-caption.text-grey.mb-2 {{ $t('settings.language') }}
|
||||
v-btn-toggle.d-flex.w-100.border-subtle(
|
||||
v-model="$i18n.locale"
|
||||
mandatory
|
||||
rounded="lg"
|
||||
color="#00cec9"
|
||||
)
|
||||
v-btn.flex-grow-1(value="en") EN
|
||||
v-btn.flex-grow-1(value="de") DE
|
||||
v-btn.flex-grow-1(value="ja") JA
|
||||
|
||||
.lang-switch-wrapper.border-subtle.mb-2
|
||||
.lang-glider(:style="{ transform: `translateX(${localeIndex * 100}%)` }")
|
||||
button.lang-btn(
|
||||
v-for="l in availableLocales"
|
||||
:key="l"
|
||||
:class="{ 'active': tempLocale === l }"
|
||||
@click="tempLocale = l"
|
||||
) {{ l.toUpperCase() }}
|
||||
|
||||
v-card-actions.mt-2
|
||||
v-btn.text-white(
|
||||
@@ -256,7 +283,9 @@
|
||||
|
||||
<script setup>
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
import {
|
||||
ref, watch, onMounted, computed, onUnmounted,
|
||||
} from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useAppStore } from '@/stores/appStore';
|
||||
import logo from '@/assets/icon.svg';
|
||||
@@ -265,8 +294,10 @@ const drawer = ref(false);
|
||||
const { locale, t } = useI18n();
|
||||
const store = useAppStore();
|
||||
|
||||
const viewState = ref('login');
|
||||
const mousePos = ref({ x: 0, y: 0 });
|
||||
|
||||
const inputKey = ref('');
|
||||
const loggingIn = ref(false);
|
||||
const syncing = ref(false);
|
||||
const errorMsg = ref('');
|
||||
const showSettings = ref(false);
|
||||
@@ -276,16 +307,114 @@ const snackbar = ref({ show: false, text: '', color: 'success' });
|
||||
const tempBatchSize = ref(store.batchSize);
|
||||
const tempDrawingAccuracy = ref(store.drawingAccuracy);
|
||||
|
||||
const availableLocales = ['en', 'de', 'ja'];
|
||||
const tempLocale = ref(locale.value);
|
||||
const localeIndex = computed(() => availableLocales.indexOf(tempLocale.value));
|
||||
|
||||
const backgroundKanjis = [
|
||||
{
|
||||
char: '禅',
|
||||
style: {
|
||||
top: '15%', left: '10%', fontSize: '8rem', opacity: 0.03,
|
||||
},
|
||||
},
|
||||
{
|
||||
char: '空',
|
||||
style: {
|
||||
top: '60%', left: '85%', fontSize: '10rem', opacity: 0.03,
|
||||
},
|
||||
},
|
||||
{
|
||||
char: '無',
|
||||
style: {
|
||||
top: '30%', left: '60%', fontSize: '12rem', opacity: 0.02,
|
||||
},
|
||||
},
|
||||
{
|
||||
char: '心',
|
||||
style: {
|
||||
top: '80%', left: '20%', fontSize: '6rem', opacity: 0.04,
|
||||
},
|
||||
},
|
||||
{
|
||||
char: '道',
|
||||
style: {
|
||||
top: '10%', left: '70%', fontSize: '5rem', opacity: 0.05,
|
||||
},
|
||||
},
|
||||
{
|
||||
char: '学',
|
||||
style: {
|
||||
top: '50%', left: '5%', fontSize: '4rem', opacity: 0.04,
|
||||
},
|
||||
},
|
||||
{
|
||||
char: '夢',
|
||||
style: {
|
||||
top: '90%', left: '60%', fontSize: '7rem', opacity: 0.03,
|
||||
},
|
||||
},
|
||||
{
|
||||
char: '力',
|
||||
style: {
|
||||
top: '25%', left: '35%', fontSize: '3rem', opacity: 0.05,
|
||||
},
|
||||
},
|
||||
{
|
||||
char: '月',
|
||||
style: {
|
||||
top: '75%', left: '90%', fontSize: '4rem', opacity: 0.04,
|
||||
},
|
||||
},
|
||||
{
|
||||
char: '水',
|
||||
style: {
|
||||
top: '40%', left: '80%', fontSize: '5rem', opacity: 0.03,
|
||||
},
|
||||
},
|
||||
{
|
||||
char: '火',
|
||||
style: {
|
||||
top: '70%', left: '50%', fontSize: '9rem', opacity: 0.02,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const handleMouseMove = (e) => {
|
||||
mousePos.value = {
|
||||
x: (e.clientX / window.innerWidth) * 2 - 1,
|
||||
y: (e.clientY / window.innerHeight) * 2 - 1,
|
||||
};
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (store.token) {
|
||||
viewState.value = 'app';
|
||||
store.fetchStats();
|
||||
} else {
|
||||
viewState.value = 'login';
|
||||
}
|
||||
window.addEventListener('mousemove', handleMouseMove);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
});
|
||||
|
||||
const parallaxStyle = computed(() => {
|
||||
if (viewState.value === 'app') return {};
|
||||
const x = mousePos.value.x * -20;
|
||||
const y = mousePos.value.y * -20;
|
||||
return {
|
||||
transform: `translate(${x}px, ${y}px) scale(1.1)`,
|
||||
};
|
||||
});
|
||||
|
||||
watch(showSettings, (isOpen) => {
|
||||
if (isOpen) {
|
||||
tempBatchSize.value = store.batchSize;
|
||||
tempDrawingAccuracy.value = store.drawingAccuracy;
|
||||
tempLocale.value = locale.value;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -305,58 +434,223 @@ async function manualSync() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogin() {
|
||||
async function triggerLogin() {
|
||||
if (!inputKey.value) return;
|
||||
loggingIn.value = true;
|
||||
viewState.value = 'authenticating';
|
||||
errorMsg.value = '';
|
||||
|
||||
try {
|
||||
const result = await store.login(inputKey.value.trim());
|
||||
if (result.user && !result.user.lastSync) {
|
||||
manualSync();
|
||||
}
|
||||
|
||||
// Wait for authentication state to settle
|
||||
setTimeout(() => {
|
||||
// Step 1: Start the zoom animation on the login card/background
|
||||
viewState.value = 'zooming';
|
||||
|
||||
// Step 2: Only after a significant part of the zoom has happened,
|
||||
// let the app content mount and start its own internal fade-in
|
||||
setTimeout(() => {
|
||||
viewState.value = 'app';
|
||||
if (result.user && !result.user.lastSync) {
|
||||
manualSync();
|
||||
}
|
||||
}, 1000); // Increased delay to ensure zoom is the primary visual focus first
|
||||
}, 800);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
errorMsg.value = e.message || t('login.failed');
|
||||
} finally {
|
||||
loggingIn.value = false;
|
||||
viewState.value = 'login';
|
||||
}
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
store.saveSettings({
|
||||
batchSize: tempBatchSize.value,
|
||||
drawingAccuracy: tempDrawingAccuracy.value,
|
||||
});
|
||||
|
||||
localStorage.setItem('zen_locale', locale.value);
|
||||
|
||||
showSettings.value = false;
|
||||
store.fetchQueue();
|
||||
|
||||
setTimeout(() => {
|
||||
if (locale.value !== tempLocale.value) {
|
||||
locale.value = tempLocale.value;
|
||||
localStorage.setItem('zen_locale', locale.value);
|
||||
}
|
||||
|
||||
store.saveSettings({
|
||||
batchSize: tempBatchSize.value,
|
||||
drawingAccuracy: tempDrawingAccuracy.value,
|
||||
});
|
||||
|
||||
store.fetchQueue();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function handleLogout() {
|
||||
showLogoutDialog.value = true;
|
||||
drawer.value = false;
|
||||
}
|
||||
|
||||
function confirmLogout() {
|
||||
store.logout();
|
||||
inputKey.value = '';
|
||||
showLogoutDialog.value = false;
|
||||
viewState.value = 'login';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" src="@/styles/pages/_app.scss"></style>
|
||||
<style lang="scss">
|
||||
.zen-app {
|
||||
position: relative;
|
||||
background-color: #121212 !important;
|
||||
}
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.safe-area-header {
|
||||
padding-top: env(safe-area-inset-top);
|
||||
.parallax-bg {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 0;
|
||||
transition: transform 0.2s ease-out;
|
||||
background: radial-gradient(circle at 50% 50%, #1a1a20 0%, #0a0a0c 100%);
|
||||
overflow: hidden;
|
||||
|
||||
height: auto !important;
|
||||
&.zooming {
|
||||
transition: transform 1.2s cubic-bezier(0.7, 0, 0.3, 1);
|
||||
}
|
||||
|
||||
:deep(.v-toolbar__content) {
|
||||
min-height: 64px;
|
||||
align-items: center;
|
||||
.bg-grid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: radial-gradient(rgba(0, 206, 201, 0.15) 1px, transparent 1px);
|
||||
background-size: 40px 40px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.kanji-layer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bg-kanji {
|
||||
position: absolute;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-weight: 900;
|
||||
font-family: "Noto Serif JP", serif;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.orb {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
filter: blur(80px);
|
||||
mix-blend-mode: screen;
|
||||
opacity: 0.6;
|
||||
animation: blob 10s infinite alternate;
|
||||
transition: all 1.2s ease-in-out;
|
||||
z-index: 0;
|
||||
|
||||
&.zooming {
|
||||
transform: scale(5);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.orb-1 {
|
||||
top: 20%;
|
||||
left: 20%;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background: rgba(0, 206, 201, 0.15);
|
||||
}
|
||||
|
||||
.orb-2 {
|
||||
top: 30%;
|
||||
right: 20%;
|
||||
width: 350px;
|
||||
height: 350px;
|
||||
background: rgba(162, 155, 254, 0.15);
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
.orb-3 {
|
||||
bottom: 10%;
|
||||
left: 30%;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background: rgba(30, 144, 255, 0.1);
|
||||
animation-delay: 4s;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blob {
|
||||
0% {
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(30px, -50px) scale(1.1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-20px, 20px) scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.login-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 50;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 1.2s cubic-bezier(0.7, 0, 0.3, 1);
|
||||
transform-origin: center center;
|
||||
|
||||
&.zooming {
|
||||
transform: scale(15);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
position: relative;
|
||||
background: rgba(30, 30, 36, 0.6) !important;
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
||||
transition: transform 0.5s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.01);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app-content-wrapper {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
filter: blur(10px);
|
||||
/* Increased blur for a smoother transition */
|
||||
transition: all 1.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
/* Match the zoom ease */
|
||||
pointer-events: none;
|
||||
/* Prevent clicks during transition */
|
||||
|
||||
&.app-entering {
|
||||
opacity: 0;
|
||||
/* Keep hidden initially even if class is present */
|
||||
}
|
||||
|
||||
&.app-visible {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
filter: none;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user