Enhance UI components and improve user notifications
- Updated App.vue to include a toast notification system for new messages, enhancing user engagement. - Modified SplashScreen.vue to streamline the intro text display with improved typing effects. - Added Montserrat font styles in style.css for better typography across the application. - Improved controller navigation in useControllerNav.ts to support enhanced focus management and sound feedback. - Updated routing logic in index.ts to redirect authenticated users from the login page to the home page. - Enhanced the Login.vue view with transition effects for a smoother user experience during login and setup processes.
This commit is contained in:
@@ -20,7 +20,19 @@
|
||||
<source src="/assets/video/video-intro.mp4?v=7" type="video/mp4">
|
||||
</video>
|
||||
|
||||
<!-- Static image background for other routes (not using video) -->
|
||||
<!-- Login: static background + archipelago-style glitch (no zoom) -->
|
||||
<template v-else-if="isLoginRoute">
|
||||
<div
|
||||
class="bg-layer bg-login-static bg-fullwidth"
|
||||
:style="{ backgroundImage: `url('/assets/img/${loginBackground}')` }"
|
||||
/>
|
||||
<!-- Archipelago-style glitch overlays - continuous every 5s -->
|
||||
<div class="login-glitch-layer login-glitch-1" :style="{ backgroundImage: `url('/assets/img/${loginBackground}')` }" />
|
||||
<div class="login-glitch-layer login-glitch-2" :style="{ backgroundImage: `url('/assets/img/${loginBackground}')` }" />
|
||||
<div class="login-glitch-scan" />
|
||||
</template>
|
||||
|
||||
<!-- Static image background for other routes (with zoom on transition) -->
|
||||
<div
|
||||
v-else
|
||||
class="bg-layer bg-zoom"
|
||||
@@ -29,9 +41,8 @@
|
||||
:key="currentBackground"
|
||||
></div>
|
||||
|
||||
|
||||
<!-- Glitch overlay layer - only for non-video backgrounds -->
|
||||
<div v-show="isGlitching && !useVideoBackground" class="bg-glitch-layer"></div>
|
||||
<!-- Glitch overlay layer - only for non-video, non-login background changes -->
|
||||
<div v-show="isGlitching && !useVideoBackground && !isLoginRoute" class="bg-glitch-layer"></div>
|
||||
</div>
|
||||
|
||||
<!-- Content with 3D transitions -->
|
||||
@@ -52,6 +63,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted, computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { resumeAudioContext, startSynthwave } from '@/composables/useLoginSounds'
|
||||
|
||||
const route = useRoute()
|
||||
const currentBackground = ref('bg-4.jpg')
|
||||
@@ -59,9 +71,12 @@ const isGlitching = ref(false)
|
||||
const isTransitioning = ref(false)
|
||||
const videoElement = ref<HTMLVideoElement | null>(null)
|
||||
|
||||
// Routes that should use video background (smooth transition from splash)
|
||||
// Routes that should use video background (smooth transition from splash, loops through login)
|
||||
const videoBackgroundRoutes = ['/onboarding/intro', '/login']
|
||||
|
||||
// Login uses video when coming from splash, or static + glitch when direct
|
||||
const isLoginRoute = computed(() => route.path === '/login')
|
||||
|
||||
// Check if current route should use video background
|
||||
const useVideoBackground = computed(() => {
|
||||
return videoBackgroundRoutes.includes(route.path)
|
||||
@@ -77,9 +92,11 @@ const routeBackgrounds: Record<string, string> = {
|
||||
'/onboarding/backup': 'bg-7.jpg',
|
||||
'/onboarding/verify': 'bg-2.jpg',
|
||||
'/onboarding/done': 'bg-1.jpg',
|
||||
'/login': 'bg-1.jpg' // Video will be used instead
|
||||
'/login': 'bg-4.jpg' // Video loops from splash (same as intro)
|
||||
}
|
||||
|
||||
const loginBackground = 'bg-1.jpg'
|
||||
|
||||
// Restore video time from splash screen for seamless transition
|
||||
function restoreVideoTime() {
|
||||
if (videoElement.value && useVideoBackground.value) {
|
||||
@@ -100,14 +117,7 @@ function restoreVideoTime() {
|
||||
|
||||
// If video was playing, ensure it continues playing immediately
|
||||
if (wasPlaying) {
|
||||
// Use requestAnimationFrame for smoother transition
|
||||
requestAnimationFrame(() => {
|
||||
if (videoElement.value) {
|
||||
videoElement.value.play().catch(err => {
|
||||
console.warn('Video play failed after time restore:', err)
|
||||
})
|
||||
}
|
||||
})
|
||||
requestAnimationFrame(() => ensureVideoPlaying())
|
||||
}
|
||||
|
||||
// Clean up session storage after successful restore
|
||||
@@ -137,6 +147,18 @@ function restoreVideoTime() {
|
||||
}
|
||||
}
|
||||
|
||||
// Video play with retry - hardened for reliability
|
||||
function ensureVideoPlaying(retries = 3): void {
|
||||
const vid = videoElement.value
|
||||
if (!vid || !useVideoBackground.value) return
|
||||
if (!vid.paused) return
|
||||
vid.play()
|
||||
.then(() => {})
|
||||
.catch(() => {
|
||||
if (retries > 0) setTimeout(() => ensureVideoPlaying(retries - 1), 300)
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure video plays when route uses video background
|
||||
watch([useVideoBackground, route], ([useVideo]) => {
|
||||
if (useVideo && videoElement.value) {
|
||||
@@ -148,9 +170,7 @@ watch([useVideoBackground, route], ([useVideo]) => {
|
||||
// Then ensure it's playing - use double RAF for smoother transition
|
||||
requestAnimationFrame(() => {
|
||||
if (videoElement.value && videoElement.value.paused) {
|
||||
videoElement.value.play().catch(err => {
|
||||
console.warn('Video autoplay failed:', err)
|
||||
})
|
||||
ensureVideoPlaying()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -169,9 +189,7 @@ onMounted(() => {
|
||||
// Use double RAF for smoother playback start
|
||||
requestAnimationFrame(() => {
|
||||
if (videoElement.value) {
|
||||
videoElement.value.play().catch(err => {
|
||||
console.warn('Video autoplay failed on mount:', err)
|
||||
})
|
||||
ensureVideoPlaying()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -213,9 +231,7 @@ watch(() => route.path, (newPath, oldPath) => {
|
||||
if (oldUsesVideo && newUsesVideo && videoElement.value) {
|
||||
// Video continues seamlessly, just ensure it's playing
|
||||
if (videoElement.value.paused) {
|
||||
videoElement.value.play().catch(err => {
|
||||
console.warn('Video play failed on route change:', err)
|
||||
})
|
||||
ensureVideoPlaying()
|
||||
}
|
||||
// No glitch effect, no zoom, no transitions for video-to-video
|
||||
isGlitching.value = false
|
||||
@@ -229,6 +245,14 @@ watch(() => route.path, (newPath, oldPath) => {
|
||||
isTransitioning.value = false
|
||||
}
|
||||
|
||||
// Login route: set background immediately, no zoom, no transition (glitch is always-on)
|
||||
if (newPath === '/login') {
|
||||
currentBackground.value = 'bg-1.jpg'
|
||||
isTransitioning.value = false
|
||||
isGlitching.value = false
|
||||
return
|
||||
}
|
||||
|
||||
// Only update if we have a defined background for this route and it's different
|
||||
if (newBg && newBg !== currentBackground.value) {
|
||||
// Trigger zoom animation ONLY for non-video routes (never for video)
|
||||
@@ -260,11 +284,8 @@ watch(() => route.path, (newPath, oldPath) => {
|
||||
// Prevent video from pausing during transitions
|
||||
function handleVideoPause(event: Event) {
|
||||
if (useVideoBackground.value && videoElement.value) {
|
||||
// Prevent pause and immediately resume playback
|
||||
event.preventDefault()
|
||||
videoElement.value.play().catch(err => {
|
||||
console.warn('Video play failed after pause prevention:', err)
|
||||
})
|
||||
ensureVideoPlaying()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,14 +293,12 @@ function handleVideoPause(event: Event) {
|
||||
function handleVideoEnded() {
|
||||
if (useVideoBackground.value && videoElement.value) {
|
||||
videoElement.value.currentTime = 0
|
||||
videoElement.value.play().catch(err => {
|
||||
console.warn('Video play failed after loop:', err)
|
||||
})
|
||||
ensureVideoPlaying()
|
||||
}
|
||||
}
|
||||
|
||||
// Update body class to disable global glitch effects ONLY for video backgrounds
|
||||
// This class is ONLY added on /onboarding/intro and /login routes
|
||||
// This class is ONLY added on /onboarding/intro (login uses its own glitch)
|
||||
// All other routes will have glitch effects enabled (normal behavior)
|
||||
watch(useVideoBackground, (usesVideo) => {
|
||||
if (usesVideo) {
|
||||
@@ -299,11 +318,20 @@ onMounted(() => {
|
||||
currentBackground.value = bg
|
||||
}
|
||||
|
||||
// Ensure no transitions or effects on mount for video backgrounds
|
||||
if (useVideoBackground.value) {
|
||||
isTransitioning.value = false
|
||||
isGlitching.value = false
|
||||
document.body.classList.add('video-background-active')
|
||||
if (sessionStorage.getItem('archipelago_from_splash') !== '1') {
|
||||
startSynthwave()
|
||||
}
|
||||
const unlock = () => {
|
||||
resumeAudioContext()
|
||||
document.removeEventListener('click', unlock)
|
||||
document.removeEventListener('touchstart', unlock)
|
||||
}
|
||||
document.addEventListener('click', unlock, { once: true })
|
||||
document.addEventListener('touchstart', unlock, { once: true })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -338,16 +366,16 @@ onMounted(() => {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Forward transition: Current screen pulls forward, new screen emerges from back */
|
||||
/* 2advanced-style: fluid depth transitions */
|
||||
.depth-forward-enter-active.view-wrapper,
|
||||
.depth-forward-leave-active.view-wrapper {
|
||||
transition: all 0.7s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
transition: all 0.9s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.depth-forward-enter-from.view-wrapper {
|
||||
opacity: 0;
|
||||
transform: translateZ(-1500px) scale(0.5);
|
||||
filter: blur(8px);
|
||||
transform: translateZ(-1200px) scale(0.6);
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
.depth-forward-enter-to.view-wrapper {
|
||||
@@ -364,13 +392,13 @@ onMounted(() => {
|
||||
|
||||
.depth-forward-leave-to.view-wrapper {
|
||||
opacity: 0;
|
||||
transform: translateZ(600px) scale(1.4);
|
||||
filter: blur(12px);
|
||||
transform: translateZ(500px) scale(1.25);
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
/* Background zoom effect - makes you feel like you're going deeper */
|
||||
/* Background zoom - 2advanced fluid */
|
||||
.bg-zoom {
|
||||
transition: transform 1.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition: transform 1.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
@@ -378,14 +406,14 @@ onMounted(() => {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
/* Enhanced effect with rotation for more console-like feel */
|
||||
/* Subtle 3D tilt - 2advanced layered depth */
|
||||
@media (min-width: 768px) {
|
||||
.depth-forward-enter-from.view-wrapper {
|
||||
transform: translateZ(-1500px) scale(0.5) rotateX(15deg);
|
||||
transform: translateZ(-1200px) scale(0.6) rotateX(6deg);
|
||||
}
|
||||
|
||||
.depth-forward-leave-to.view-wrapper {
|
||||
transform: translateZ(600px) scale(1.4) rotateX(-10deg);
|
||||
transform: translateZ(500px) scale(1.25) rotateX(-4deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,6 +425,16 @@ onMounted(() => {
|
||||
perspective-origin: 50% 50%;
|
||||
z-index: -10;
|
||||
overflow: hidden;
|
||||
min-width: 100vw;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
/* Full width background on every screen */
|
||||
.bg-fullwidth {
|
||||
min-width: 100vw;
|
||||
width: 100vw;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.bg-layer {
|
||||
@@ -425,6 +463,79 @@ video.bg-layer {
|
||||
transform: translateZ(0) scale(1);
|
||||
}
|
||||
|
||||
/* Login: static background - just there, no zoom */
|
||||
.bg-login-static {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* Archipelago-style glitch overlays for login - continuous every 5s */
|
||||
.login-glitch-layer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.login-glitch-1 {
|
||||
mix-blend-mode: screen;
|
||||
filter: hue-rotate(22deg) saturate(1.35);
|
||||
animation: login-glitch-shift 5s steps(10, end) infinite;
|
||||
}
|
||||
|
||||
.login-glitch-2 {
|
||||
mix-blend-mode: screen;
|
||||
filter: hue-rotate(-30deg) saturate(1.45);
|
||||
animation: login-glitch-shift-2 5s steps(9, end) infinite;
|
||||
}
|
||||
|
||||
.login-glitch-scan {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 6;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255,255,255,0.16), rgba(0,0,0,0) 60%),
|
||||
repeating-linear-gradient(180deg, rgba(255,255,255,0.05) 0 2px, rgba(0,0,0,0) 2px 4px),
|
||||
radial-gradient(ellipse at center, rgba(0,0,0,0) 40%, rgba(0,0,0,0.35) 100%);
|
||||
opacity: 0;
|
||||
animation: login-glitch-scan 5s ease-out infinite;
|
||||
}
|
||||
|
||||
@keyframes login-glitch-shift {
|
||||
0%, 82% { transform: translate(0,0); clip-path: inset(0% 0 0 0); opacity: 0; }
|
||||
82.1% { opacity: 0.22; }
|
||||
84% { transform: translate(6px,-2px); clip-path: inset(8% 0 70% 0); }
|
||||
86% { transform: translate(-5px,2px); clip-path: inset(42% 0 40% 0); }
|
||||
88% { transform: translate(3px,0); clip-path: inset(68% 0 10% 0); }
|
||||
91% { transform: translate(-4px,3px); clip-path: inset(18% 0 60% 0); }
|
||||
93% { transform: translate(5px,-3px); clip-path: inset(55% 0 20% 0); }
|
||||
95% { transform: translate(-3px,1px); clip-path: inset(10% 0 80% 0); }
|
||||
100% { transform: translate(0,0); clip-path: inset(0% 0 0 0); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes login-glitch-shift-2 {
|
||||
0%, 82% { transform: translate(0,0); clip-path: inset(0% 0 0 0); opacity: 0; }
|
||||
82.1% { opacity: 0.18; }
|
||||
84% { transform: translate(-6px,2px); clip-path: inset(12% 0 65% 0); }
|
||||
86% { transform: translate(5px,-1px) skewX(0.6deg); clip-path: inset(36% 0 42% 0); }
|
||||
89% { transform: translate(-3px,2px); clip-path: inset(72% 0 8% 0); }
|
||||
92% { transform: translate(4px,-3px); clip-path: inset(22% 0 58% 0); }
|
||||
95% { transform: translate(-4px,1px); clip-path: inset(50% 0 26% 0); }
|
||||
100% { transform: translate(0,0); clip-path: inset(0% 0 0 0); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes login-glitch-scan {
|
||||
0%, 82% { opacity: 0; transform: translateY(-20%); }
|
||||
84% { opacity: 0.4; }
|
||||
90% { opacity: 0.28; }
|
||||
100% { opacity: 0; transform: translateY(115%); }
|
||||
}
|
||||
|
||||
/* Glitch overlay layer */
|
||||
.bg-glitch-layer {
|
||||
position: absolute;
|
||||
|
||||
Reference in New Issue
Block a user