Enhance audio and visual elements in the UI for improved user experience
- Added a new script in package.json to generate welcome speech audio for enhanced onboarding. - Updated SplashScreen.vue and OnboardingWrapper.vue to use the new intro background image and poster. - Modified Dashboard.vue and Login.vue to reflect changes in background images for consistency. - Removed outdated background images and updated references to ensure a cohesive visual theme. - Improved tap-to-start feature with new text and logo in SplashScreen.vue for better engagement. - Enhanced audio playback functionality in useLoginSounds.ts to include welcome speech.
This commit is contained in:
@@ -12,14 +12,14 @@
|
||||
muted
|
||||
playsinline
|
||||
preload="auto"
|
||||
poster="/assets/img/bg-4.jpg"
|
||||
poster="/assets/img/bg-intro.jpg"
|
||||
>
|
||||
<source src="/assets/video/video-intro.mp4?v=7" type="video/mp4">
|
||||
<!-- Fallback to image if video fails -->
|
||||
<div
|
||||
class="absolute inset-0"
|
||||
:style="{
|
||||
backgroundImage: 'url(/assets/img/bg-4.jpg)',
|
||||
backgroundImage: 'url(/assets/img/bg-intro.jpg)',
|
||||
backgroundSize: 'auto 100vh',
|
||||
backgroundPosition: 'center top',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
@@ -32,7 +32,7 @@
|
||||
v-else
|
||||
class="absolute inset-0"
|
||||
:style="{
|
||||
backgroundImage: 'url(/assets/img/bg-4.jpg)',
|
||||
backgroundImage: 'url(/assets/img/bg-intro.jpg)',
|
||||
backgroundSize: 'auto 100vh',
|
||||
backgroundPosition: 'center top',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
@@ -96,15 +96,20 @@
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- Tap to start - required for audio (browser autoplay policy) -->
|
||||
<!-- Tap to start - logo + "Enter the Exit" behind (like screensaver) -->
|
||||
<div
|
||||
v-if="showTapToStart"
|
||||
class="absolute inset-0 z-[100] flex items-center justify-center bg-black/40 cursor-pointer"
|
||||
@click="handleTapToStart"
|
||||
>
|
||||
<p class="font-mono text-white/90 text-lg sm:text-xl px-6 py-4 rounded-lg border border-white/20 bg-black/30 backdrop-blur-sm">
|
||||
Tap to start
|
||||
</p>
|
||||
<div class="tap-to-start-content relative flex items-center justify-center">
|
||||
<span class="tap-to-start-text font-archipelago font-extrabold text-[rgba(0,0,0,0.35)] text-6xl sm:text-7xl md:text-8xl lg:text-9xl tracking-widest uppercase whitespace-nowrap select-none">
|
||||
Enter the Exit
|
||||
</span>
|
||||
<div class="tap-to-start-logo absolute">
|
||||
<ScreensaverLogo />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Skip Button -->
|
||||
@@ -121,7 +126,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
||||
import { playIntroTyping, playLoopStart, resumeAudioContext, startSynthwave, stopIntroTyping } from '@/composables/useLoginSounds'
|
||||
import ScreensaverLogo from '@/components/ScreensaverLogo.vue'
|
||||
import { playIntroTyping, playLoopStart, playWelcomeNoderunnerSpeech, resumeAudioContext, startSynthwave, stopIntroTyping } from '@/composables/useLoginSounds'
|
||||
|
||||
const emit = defineEmits<{
|
||||
complete: []
|
||||
@@ -250,7 +256,8 @@ function skipIntro() {
|
||||
stopIntroTyping()
|
||||
playLoopStart()
|
||||
startSynthwave()
|
||||
|
||||
playWelcomeNoderunnerSpeech()
|
||||
|
||||
// Stop alien intro typing and any playing typing sound
|
||||
stopIntroTyping()
|
||||
isTypingLine1.value = false
|
||||
@@ -357,6 +364,7 @@ function startAlienIntro() {
|
||||
stopIntroTyping()
|
||||
playLoopStart()
|
||||
startSynthwave()
|
||||
playWelcomeNoderunnerSpeech()
|
||||
if (videoElement.value) {
|
||||
videoElement.value.play().catch(err => {
|
||||
console.warn('Video autoplay failed on welcome:', err)
|
||||
@@ -564,5 +572,44 @@ onBeforeUnmount(() => {
|
||||
.bg-zoom-transition.bg-zoom-in {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
/* Tap to start - "Enter the Exit" big behind logo */
|
||||
.tap-to-start-content {
|
||||
min-height: 12rem;
|
||||
}
|
||||
.tap-to-start-text {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.tap-to-start-logo {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.tap-to-start-logo {
|
||||
filter: drop-shadow(0 0 40px rgba(255, 255, 255, 0.15));
|
||||
}
|
||||
.tap-to-start-logo :deep(.logo-gradient-border) {
|
||||
width: 12rem;
|
||||
height: 12rem;
|
||||
}
|
||||
@media (min-width: 640px) {
|
||||
.tap-to-start-content {
|
||||
min-height: 14rem;
|
||||
}
|
||||
.tap-to-start-logo :deep(.logo-gradient-border) {
|
||||
width: 14rem;
|
||||
height: 14rem;
|
||||
}
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
.tap-to-start-content {
|
||||
min-height: 16rem;
|
||||
}
|
||||
.tap-to-start-logo :deep(.logo-gradient-border) {
|
||||
width: 16rem;
|
||||
height: 16rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -130,6 +130,18 @@ export function stopIntroTyping() {
|
||||
}
|
||||
}
|
||||
|
||||
const WELCOME_SPEECH_URL = '/assets/audio/welcome-noderunner.mp3'
|
||||
|
||||
/** Sci-fi female voice: "Welcome Noderunner" - plays when welcome text types in.
|
||||
* Requires pre-recorded audio from ElevenLabs. Run:
|
||||
* ELEVENLABS_API_KEY=your_key node neode-ui/scripts/generate-welcome-speech.js
|
||||
* Browse sci-fi voices at elevenlabs.io/voice-library and set ELEVENLABS_VOICE_ID for custom voice. */
|
||||
export function playWelcomeNoderunnerSpeech() {
|
||||
const audio = new Audio(WELCOME_SPEECH_URL)
|
||||
audio.volume = 0.9
|
||||
audio.play().catch(() => {})
|
||||
}
|
||||
|
||||
/** Typing tick - for dashboard welcome typing (typing.mp3) */
|
||||
let typingTickPool: HTMLAudioElement[] = []
|
||||
const TYPING_TICK_POOL_SIZE = 5
|
||||
|
||||
@@ -341,7 +341,7 @@ const currentBackgroundImage = computed(() => {
|
||||
if (showAppStoreBackground.value) return 'bg-appstore.jpg'
|
||||
if (showCloudBackground.value) return 'bg-cloud.jpg'
|
||||
if (showHomeBackground.value) return 'bg-home.jpg'
|
||||
return 'bg-4.jpg'
|
||||
return 'bg-intro.jpg'
|
||||
})
|
||||
|
||||
const altBackgroundImage = computed(() => {
|
||||
@@ -352,7 +352,7 @@ const altBackgroundImage = computed(() => {
|
||||
if (showAppStoreBackground.value) return 'bg-appstore.jpg'
|
||||
if (showCloudBackground.value) return 'bg-cloud.jpg'
|
||||
if (showHomeBackground.value) return 'bg-home.jpg'
|
||||
return 'bg-3.jpg'
|
||||
return 'bg-intro-3.jpg'
|
||||
})
|
||||
|
||||
// Check if overlay should be dark (0.8 opacity)
|
||||
@@ -1442,7 +1442,7 @@ aside:not(.sidebar-animate) .sidebar-logout-btn {
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
/* Default state - bg-4 visible, bg-3 hidden back */
|
||||
/* Default state - bg-intro visible, bg-intro-3 hidden back */
|
||||
.dashboard-view .bg-layer:first-of-type {
|
||||
opacity: 1;
|
||||
transform: translateZ(0) scale(1);
|
||||
|
||||
@@ -8,12 +8,8 @@
|
||||
>
|
||||
<!-- Logo - half in, half out of container -->
|
||||
<div class="absolute -top-10 left-1/2 -translate-x-1/2 z-10">
|
||||
<div class="logo-gradient-border">
|
||||
<img
|
||||
src="/assets/img/favico.svg"
|
||||
alt="Archipelago"
|
||||
class="w-20 h-20"
|
||||
/>
|
||||
<div class="logo-gradient-border w-20 h-20">
|
||||
<AnimatedLogo no-border fit />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -136,6 +132,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import AnimatedLogo from '@/components/AnimatedLogo.vue'
|
||||
import { useAppStore } from '../stores/app'
|
||||
import { useLoginTransitionStore } from '../stores/loginTransition'
|
||||
import { rpcClient } from '../api/rpc-client'
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
muted
|
||||
playsinline
|
||||
preload="auto"
|
||||
poster="/assets/img/bg-4.jpg"
|
||||
poster="/assets/img/bg-intro.jpg"
|
||||
style="width: 100%; height: 100%; object-fit: cover; object-position: center; position: absolute; inset: 0; transform: scale(1); transition: none;"
|
||||
@pause.prevent="handleVideoPause"
|
||||
@ended="handleVideoEnded"
|
||||
@@ -66,7 +66,7 @@ import { useRoute } from 'vue-router'
|
||||
import { resumeAudioContext, startSynthwave } from '@/composables/useLoginSounds'
|
||||
|
||||
const route = useRoute()
|
||||
const currentBackground = ref('bg-4.jpg')
|
||||
const currentBackground = ref('bg-intro.jpg')
|
||||
const isGlitching = ref(false)
|
||||
const isTransitioning = ref(false)
|
||||
const videoElement = ref<HTMLVideoElement | null>(null)
|
||||
@@ -83,19 +83,19 @@ const useVideoBackground = computed(() => {
|
||||
})
|
||||
|
||||
// Map each route to a specific background image
|
||||
// Note: bg-4.jpg is used for splash and /onboarding/intro for seamless transition
|
||||
// Note: bg-intro.jpg is used for splash and /onboarding/intro for seamless transition
|
||||
const routeBackgrounds: Record<string, string> = {
|
||||
'/onboarding/intro': 'bg-4.jpg', // Video will be used instead
|
||||
'/onboarding/options': 'bg-5.jpg',
|
||||
'/onboarding/path': 'bg-3.jpg',
|
||||
'/onboarding/did': 'bg-6.jpg',
|
||||
'/onboarding/backup': 'bg-7.jpg',
|
||||
'/onboarding/verify': 'bg-2.jpg',
|
||||
'/onboarding/done': 'bg-1.jpg',
|
||||
'/login': 'bg-4.jpg' // Video loops from splash (same as intro)
|
||||
'/onboarding/intro': 'bg-intro.jpg', // Video will be used instead
|
||||
'/onboarding/options': 'bg-intro-4.jpg',
|
||||
'/onboarding/path': 'bg-intro-3.jpg',
|
||||
'/onboarding/did': 'bg-intro-5.jpg',
|
||||
'/onboarding/backup': 'bg-intro-6.jpg',
|
||||
'/onboarding/verify': 'bg-intro-2.jpg',
|
||||
'/onboarding/done': 'bg-intro-1.jpg',
|
||||
'/login': 'bg-intro.jpg' // Video loops from splash (same as intro)
|
||||
}
|
||||
|
||||
const loginBackground = 'bg-1.jpg'
|
||||
const loginBackground = 'bg-intro-1.jpg'
|
||||
|
||||
// Restore video time from splash screen for seamless transition
|
||||
function restoreVideoTime() {
|
||||
@@ -247,7 +247,7 @@ watch(() => route.path, (newPath, oldPath) => {
|
||||
|
||||
// Login route: set background immediately, no zoom, no transition (glitch is always-on)
|
||||
if (newPath === '/login') {
|
||||
currentBackground.value = 'bg-1.jpg'
|
||||
currentBackground.value = 'bg-intro-1.jpg'
|
||||
isTransitioning.value = false
|
||||
isGlitching.value = false
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user