1
0
Fork 0
moritzruth.de/src/App.vue
2021-12-18 21:49:30 +01:00

168 lines
4.6 KiB
Vue

<template>
<div class="w-100vw text-light-100 overflow-x-hidden">
<router-view v-slot="{ Component }">
<suspense @pending="startLoading()" @resolve="stopLoading()">
<!-- The key makes that components are not reused if only params changed -->
<component :is="Component" :key="$route.fullPath"/>
</suspense>
</router-view>
</div>
<div class="bg-background fixed top-0 left-0 right-0 bottom-0 z-100 backdrop-filter" :style="loadingOverlayStyle">
<div class="font-bold text-light-900 text-2xl h-full w-full flex justify-center items-center overflow-hidden" :style="loadingOverlayContentStyle">
<div :class="$style.centerLoadingText">
Loading...
</div>
<div
v-for="loadingText in loadingTexts"
:key="loadingText.x + '|' + loadingText.y"
:style="{ top: loadingText.x + 'px', left: loadingText.y + 'px' }"
class="absolute"
:class="$style.loadingText"
>
Loading...
</div>
</div>
</div>
</template>
<style module>
html, body {
@apply bg-background;
overflow-x: hidden;
width: 100vw;
min-height: 100vh;
}
.centerLoadingText {
animation: fade-in 2s ease-out both;
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.loadingText {
animation: fade-in-out 8s ease both;
}
@keyframes fade-in-out {
0%, 100% {
opacity: 0;
}
50% {
opacity: 1;
}
}
</style>
<script lang="ts">
import { computed, ref } from "vue"
import { useIntervalFn, useWindowSize, whenever } from "@vueuse/core"
import { useRouter } from "vue-router"
import { useHead } from "@vueuse/head"
import { pageComponentLoading } from "./store"
import { getKeywordsTagWithBase } from "./data"
export default {
name: "App",
setup() {
useHead({
meta: [
getKeywordsTagWithBase([])
]
})
const loadingStartedTime = ref<null | number>(null)
const isLoadingScreenActive = ref(false)
const START_TRANSITION_DURATION = 200
const START_TRANSITION_DELAY = 0
const loadingTexts = ref<Array<{ x: number; y: number }>>([])
const stopLoading = () => {
isLoadingScreenActive.value = false
loadingStartedTime.value = null
}
const startLoading = () => {
if (loadingStartedTime.value !== null) return
loadingStartedTime.value = Date.now()
isLoadingScreenActive.value = true
loadingTexts.value = []
}
// Suspense @resolve is also called when the component is not async, so we don't need to handle stopping
whenever(pageComponentLoading, () => {
// Runs when a page component is imported
startLoading()
})
const router = useRouter()
let isFirst = true
router.beforeResolve((to, from, next) => {
if (isFirst) {
isFirst = false
next()
return
}
startLoading()
setTimeout(next, START_TRANSITION_DURATION)
})
const { width, height } = useWindowSize()
useIntervalFn(() => {
const start = loadingStartedTime.value
if (start === null) return
if (Date.now() - start > 3000) {
if (loadingTexts.value.length > 500) loadingTexts.value = []
loadingTexts.value = [...loadingTexts.value, { x: Math.random() * width.value, y: Math.random() * height.value }]
}
}, 800)
return {
isLoadingScreenActive,
startLoading,
stopLoading,
loadingTexts,
loadingOverlayStyle: computed(() => {
if (isLoadingScreenActive.value) {
return {
"--tw-bg-opacity": 1,
"--tw-backdrop-blur": "blur(20px)",
"transitionDelay": `${START_TRANSITION_DELAY}ms`,
"transition": `background ${START_TRANSITION_DURATION}ms ease, backdrop-filter 100ms linear`
}
}
return {
"--tw-bg-opacity": 0,
"--tw-backdrop-blur": 0,
"transition": "background 200ms ease, backdrop-filter 200ms linear",
"pointerEvents": "none"
}
}),
loadingOverlayContentStyle: computed(() => {
if (isLoadingScreenActive.value) {
return {
opacity: 1,
transition: "opacity 2s ease-out",
transitionDelay: "1s"
}
}
return {
opacity: 0,
transition: "opacity 100ms ease-out",
pointerEvents: "none"
}
})
}
}
}
</script>