feat(docs): add welcome screen with animated logo
Introduce a new welcome screen that automatically redirects first-time visitors from the homepage. The screen features an animated "Easy Vibe" logo with three color themes (ocean, rainbow, sunset) that cycle through a drawing animation. Users can click anywhere to enter the main site. The welcome screen includes: - A JSON file containing SVG path data for the animated logo - A Vue component with gradient backgrounds and smooth animations - Logic to detect first-time visitors using localStorage - Integration into the existing VitePress theme structure - Updated navigation to exclude the welcome page from sidebar controls - Modified homepage logic to redirect to welcome screen on first visit
This commit is contained in:
@@ -0,0 +1,319 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useRouter, withBase } from 'vitepress'
|
||||
import easyVibePaths from '../data/easyVibePaths.json'
|
||||
|
||||
const router = useRouter()
|
||||
const WELCOME_SEEN_KEY = 'easy-vibe-welcome-seen'
|
||||
const phase = ref('reset')
|
||||
const theme = ref('ocean')
|
||||
const themes = ['ocean', 'rainbow', 'sunset']
|
||||
let timers = []
|
||||
|
||||
const themeColor = computed(() => `url(#welcome-${theme.value})`)
|
||||
const themeClass = computed(() => `welcome-theme-${theme.value}`)
|
||||
|
||||
const clearTimers = () => {
|
||||
timers.forEach((timer) => clearTimeout(timer))
|
||||
timers = []
|
||||
}
|
||||
|
||||
const runLoop = () => {
|
||||
clearTimers()
|
||||
const run = () => {
|
||||
phase.value = 'draw'
|
||||
timers.push(
|
||||
setTimeout(() => {
|
||||
phase.value = 'fade'
|
||||
}, 6200)
|
||||
)
|
||||
timers.push(
|
||||
setTimeout(() => {
|
||||
phase.value = 'reset'
|
||||
}, 7600)
|
||||
)
|
||||
timers.push(
|
||||
setTimeout(() => {
|
||||
const currentIndex = themes.indexOf(theme.value)
|
||||
theme.value = themes[(currentIndex + 1) % themes.length]
|
||||
run()
|
||||
}, 7800)
|
||||
)
|
||||
}
|
||||
timers.push(setTimeout(run, 80))
|
||||
}
|
||||
|
||||
const enterHome = () => {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const nextPath = params.get('next')
|
||||
window.localStorage.setItem(WELCOME_SEEN_KEY, '1')
|
||||
if (nextPath) {
|
||||
router.go(nextPath)
|
||||
return
|
||||
}
|
||||
router.go(withBase('/'))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
runLoop()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clearTimers()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="welcome-overlay"
|
||||
:class="themeClass"
|
||||
@click="enterHome"
|
||||
>
|
||||
<div class="welcome-content">
|
||||
<div
|
||||
class="welcome-logo"
|
||||
:style="{ '--welcome-theme-color': themeColor }"
|
||||
:class="{
|
||||
'welcome-fin': phase === 'draw' || phase === 'fade',
|
||||
'welcome-fade': phase === 'fade',
|
||||
'welcome-reset': phase === 'reset'
|
||||
}"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 460 220"
|
||||
class="welcome-svg"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="welcome-rainbow"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="460"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0%" stop-color="#00a6ff" />
|
||||
<stop offset="18%" stop-color="#00c6a2" />
|
||||
<stop offset="36%" stop-color="#53d93e" />
|
||||
<stop offset="54%" stop-color="#f4c732" />
|
||||
<stop offset="72%" stop-color="#ff7a1a" />
|
||||
<stop offset="86%" stop-color="#ff3c81" />
|
||||
<stop offset="100%" stop-color="#9d4edd" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="welcome-ocean"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="460"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0%" stop-color="#06b6d4" />
|
||||
<stop offset="50%" stop-color="#0ea5e9" />
|
||||
<stop offset="100%" stop-color="#3b82f6" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="welcome-sunset"
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="460"
|
||||
y2="0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0%" stop-color="#f43f5e" />
|
||||
<stop offset="50%" stop-color="#f97316" />
|
||||
<stop offset="100%" stop-color="#f59e0b" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
v-for="(path, index) in easyVibePaths"
|
||||
:key="index"
|
||||
:d="path"
|
||||
class="welcome-path"
|
||||
:class="`welcome-path-${index}`"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="welcome-tip">
|
||||
Click anywhere to enter home
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.welcome-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
isolation: isolate;
|
||||
background:
|
||||
radial-gradient(120% 90% at 50% -20%, rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0)),
|
||||
linear-gradient(135deg, #e8f8ff 0%, #e9edff 36%, #efe7ff 68%, #ffeef4 100%);
|
||||
background-size: 130% 130%;
|
||||
background-position: 0% 0%;
|
||||
cursor: pointer;
|
||||
animation: welcome-bg-base-flow 42s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.welcome-overlay::before,
|
||||
.welcome-overlay::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -18%;
|
||||
pointer-events: none;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
.welcome-overlay::before {
|
||||
background:
|
||||
radial-gradient(60% 58% at 18% 45%, rgba(182, 225, 255, 0.32), rgba(182, 225, 255, 0)),
|
||||
radial-gradient(48% 52% at 82% 62%, rgba(223, 199, 255, 0.28), rgba(223, 199, 255, 0));
|
||||
animation: welcome-bg-wave-a 26s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.welcome-overlay::after {
|
||||
background:
|
||||
radial-gradient(54% 52% at 68% 26%, rgba(186, 245, 228, 0.24), rgba(186, 245, 228, 0)),
|
||||
radial-gradient(56% 48% at 30% 82%, rgba(255, 219, 189, 0.22), rgba(255, 219, 189, 0));
|
||||
animation: welcome-bg-wave-b 34s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.welcome-theme-ocean {
|
||||
background:
|
||||
radial-gradient(120% 90% at 50% -20%, rgba(255, 255, 255, 0.88), rgba(255, 255, 255, 0)),
|
||||
linear-gradient(135deg, #e0f7fa 0%, #e7f0ff 45%, #eef3ff 100%);
|
||||
}
|
||||
|
||||
.welcome-theme-rainbow {
|
||||
background:
|
||||
radial-gradient(120% 90% at 50% -20%, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0)),
|
||||
linear-gradient(135deg, #e8f8ff 0%, #e9edff 36%, #efe7ff 68%, #ffeef4 100%);
|
||||
}
|
||||
|
||||
.welcome-theme-sunset {
|
||||
background:
|
||||
radial-gradient(120% 90% at 50% -20%, rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0)),
|
||||
linear-gradient(135deg, #fff0e8 0%, #ffe9dc 45%, #ffe1f0 100%);
|
||||
}
|
||||
|
||||
.welcome-content {
|
||||
width: min(88vw, 700px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.welcome-logo {
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.welcome-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.welcome-path {
|
||||
fill: var(--welcome-theme-color);
|
||||
fill-opacity: 0;
|
||||
stroke: var(--welcome-theme-color);
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-dasharray: 1000;
|
||||
stroke-dashoffset: 1000;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.welcome-fin .welcome-path {
|
||||
stroke-dashoffset: 0;
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
.welcome-fin .welcome-path-0 { transition: stroke-dashoffset 0.62s ease-in-out 0s, fill-opacity 0.45s ease-in 2.75s; }
|
||||
.welcome-fin .welcome-path-1 { transition: stroke-dashoffset 0.62s ease-in-out 0.28s, fill-opacity 0.45s ease-in 2.75s; }
|
||||
.welcome-fin .welcome-path-2 { transition: stroke-dashoffset 0.62s ease-in-out 0.56s, fill-opacity 0.45s ease-in 2.75s; }
|
||||
.welcome-fin .welcome-path-3 { transition: stroke-dashoffset 0.62s ease-in-out 0.84s, fill-opacity 0.45s ease-in 2.75s; }
|
||||
.welcome-fin .welcome-path-4 { transition: stroke-dashoffset 0.62s ease-in-out 1.12s, fill-opacity 0.45s ease-in 2.75s; }
|
||||
.welcome-fin .welcome-path-5 { transition: stroke-dashoffset 0.62s ease-in-out 1.4s, fill-opacity 0.45s ease-in 2.75s; }
|
||||
.welcome-fin .welcome-path-6 { transition: stroke-dashoffset 0.62s ease-in-out 1.68s, fill-opacity 0.45s ease-in 2.75s; }
|
||||
.welcome-fin .welcome-path-7 { transition: stroke-dashoffset 0.62s ease-in-out 1.96s, fill-opacity 0.45s ease-in 2.75s; }
|
||||
.welcome-fin .welcome-path-8 { transition: stroke-dashoffset 0.62s ease-in-out 2.24s, fill-opacity 0.45s ease-in 2.75s; }
|
||||
|
||||
.welcome-fade {
|
||||
opacity: 0;
|
||||
transition: opacity 0.85s ease-out;
|
||||
}
|
||||
|
||||
.welcome-reset {
|
||||
opacity: 0;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.welcome-tip {
|
||||
margin: 24px 0 0;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.08em;
|
||||
color: rgba(34, 34, 34, 0.38);
|
||||
text-transform: uppercase;
|
||||
animation: welcome-tip-breathe 7s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes welcome-tip-breathe {
|
||||
0% {
|
||||
opacity: 0.24;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.72;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.24;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes welcome-bg-base-flow {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
100% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes welcome-bg-wave-a {
|
||||
0% {
|
||||
transform: translate3d(-2.5%, 1.8%, 0) scale(1.02);
|
||||
opacity: 0.72;
|
||||
}
|
||||
50% {
|
||||
transform: translate3d(2%, -1.6%, 0) scale(1.06);
|
||||
opacity: 0.9;
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(4%, -2.4%, 0) scale(1.08);
|
||||
opacity: 0.72;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes welcome-bg-wave-b {
|
||||
0% {
|
||||
transform: translate3d(2.2%, -1.4%, 0) scale(1.01);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
transform: translate3d(-2.6%, 1.6%, 0) scale(1.05);
|
||||
opacity: 0.86;
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(-4.4%, 2.4%, 0) scale(1.07);
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user