Refactor background image rotation in login.vue

This commit is contained in:
jxxghp
2024-10-30 18:49:24 +08:00
parent 181ad39e18
commit 74fe67fe4d

View File

@@ -32,9 +32,8 @@ const isPasswordVisible = ref(false)
const errorMessage = ref('') const errorMessage = ref('')
// 背景图片 URL 和预加载 URL // 背景图片 URL 和预加载 URL
const backgroundImageUrl = ref('')
const backgroundImages = ref<string[]>([]) const backgroundImages = ref<string[]>([])
const isImageLoaded = ref(false) const activeImageIndex = ref(0)
// 是否开启双重验证 // 是否开启双重验证
const isOTP = ref(false) const isOTP = ref(false)
@@ -49,43 +48,11 @@ let intervalTimer: NodeJS.Timeout | null = null
async function fetchBackgroundImage() { async function fetchBackgroundImage() {
try { try {
backgroundImages.value = await api.get('/login/wallpapers') backgroundImages.value = await api.get('/login/wallpapers')
if (backgroundImages.value && backgroundImages.value.length > 0) {
backgroundImages.value.sort(() => Math.random() - 0.5)
backgroundImageUrl.value = backgroundImages.value[0]
}
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }
} }
// 切换背景图片函数
function startBackgroundImageRotation() {
let currentIndex = 0
intervalTimer = setInterval(() => {
if (backgroundImages.value.length > 1) {
// 更新下一张图片索引
const nextIndex = (currentIndex + 1) % backgroundImages.value.length
const nextImageUrl = backgroundImages.value[nextIndex]
// 使用预加载机制确保下一张图片已加载完成
const img = new Image()
img.src = nextImageUrl
img.onload = () => {
// 开始淡入过渡
isImageLoaded.value = false
// 延迟一小段时间触发淡入效果
setTimeout(() => {
// 更新当前索引并切换背景图片 URL
currentIndex = nextIndex
backgroundImageUrl.value = nextImageUrl
// 切换完成后显示图片
isImageLoaded.value = true
}, 1000)
}
}
}, 5000)
}
// 查询是否开启双重验证 // 查询是否开启双重验证
const fetchOTP = debounce(async () => { const fetchOTP = debounce(async () => {
const userid = usernameInput.value?.value const userid = usernameInput.value?.value
@@ -216,6 +183,13 @@ function login() {
}) })
} }
// 初始化背景图片轮循
function startBackgroundRotation() {
intervalTimer = setInterval(() => {
activeImageIndex.value = (activeImageIndex.value + 1) % backgroundImages.value.length
}, 5000) // 每5秒切换一次图片
}
// 自动登录 // 自动登录
onMounted(async () => { onMounted(async () => {
// 从Vuex Store中获取token和remember状态 // 从Vuex Store中获取token和remember状态
@@ -228,8 +202,9 @@ onMounted(async () => {
} else { } else {
// 获取背景图片 // 获取背景图片
await fetchBackgroundImage() await fetchBackgroundImage()
// 开始背景图片定时切换 if (backgroundImages.value.length > 1) {
startBackgroundImageRotation() startBackgroundRotation()
}
} }
}) })
@@ -240,79 +215,75 @@ onUnmounted(() => {
<template> <template>
<!-- 当前背景图片 --> <!-- 当前背景图片 -->
<div class="absolute inset-0 w-full h-full overflow-hidden"> <div class="relative flex min-h-screen flex-col bg-gray-900 py-14">
<VImg <div>
:src="backgroundImageUrl" <div
:class="{ 'opacity-0': !isImageLoaded, 'opacity-100': isImageLoaded }" v-for="(imageUrl, index) in backgroundImages"
class="absolute inset-0 transition-opacity duration-1000" class="absolute-top-shift absolute inset-0 bg-cover bg-center transition-opacity duration-300 ease-in"
cover :class="{ 'opacity-100': index === activeImageIndex, 'opacity-0': index !== activeImageIndex }"
position="center top" >
/> <VImg :src="imageUrl" class="absolute inset-0 transition-opacity duration-1000" cover position="center top" />
<div <div
class="absolute inset-0" class="absolute inset-0"
style="background-image: linear-gradient(rgba(45, 55, 72, 33%) 0%, rgb(26, 32, 46) 100%)" style="background-image: linear-gradient(rgba(45, 55, 72, 47%) 0%, rgb(26, 32, 46) 100%)"
/> />
</div> </div>
</div>
<div class="auth-wrapper d-flex align-center justify-center pa-4"> <!-- 登录表单 -->
<VCard <div class="auth-wrapper d-flex align-center justify-center pa-4">
class="auth-card px-7 py-3 w-full h-full rounded-lg" <VCard class="auth-card px-7 py-3 w-full h-full rounded-lg opacity-85" max-width="24rem">
:class="{ 'opacity-85': isImageLoaded }" <VCardItem class="justify-center">
max-width="24rem" <template #prepend>
> <div class="d-flex pe-0">
<VCardItem class="justify-center"> <VImg :src="logo" width="64" height="64" />
<template #prepend> </div>
<div class="d-flex pe-0"> </template>
<VImg :src="logo" width="64" height="64" /> <VCardTitle class="font-weight-bold text-2xl text-uppercase"> MoviePilot </VCardTitle>
</div> </VCardItem>
</template> <VCardText>
<VForm ref="refForm" @submit.prevent="() => {}">
<VCardTitle class="font-weight-semibold text-2xl text-uppercase"> MoviePilot </VCardTitle> <VRow>
</VCardItem> <!-- username -->
<VCol cols="12">
<VCardText> <VTextField
<VForm ref="refForm" @submit.prevent="() => {}"> ref="usernameInput"
<VRow> v-model="form.username"
<!-- username --> label="用户名"
<VCol cols="12"> type="text"
<VTextField :rules="[requiredValidator]"
ref="usernameInput" @input="fetchOTP"
v-model="form.username" />
label="用户名" </VCol>
type="text" <!-- password -->
:rules="[requiredValidator]" <VCol cols="12">
@input="fetchOTP" <VTextField
/> v-model="form.password"
</VCol> label="密码"
<!-- password --> :type="isPasswordVisible ? 'text' : 'password'"
<VCol cols="12"> :append-inner-icon="isPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
<VTextField :rules="[requiredValidator]"
v-model="form.password" @click:append-inner="isPasswordVisible = !isPasswordVisible"
label="密码" />
:type="isPasswordVisible ? 'text' : 'password'" </VCol>
:append-inner-icon="isPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'" <VCol cols="12">
:rules="[requiredValidator]" <VTextField v-if="isOTP" v-model="form.otp_password" label="双重验证码" type="input" />
@click:append-inner="isPasswordVisible = !isPasswordVisible" <!-- remember me checkbox -->
/> <div class="d-flex align-center justify-space-between flex-wrap">
</VCol> <VCheckbox v-model="form.remember" label="保持登录" required />
<VCol cols="12"> </div>
<VTextField v-if="isOTP" v-model="form.otp_password" label="双重验证码" type="input" /> </VCol>
<!-- remember me checkbox --> <VCol cols="12">
<div class="d-flex align-center justify-space-between flex-wrap"> <!-- login button -->
<VCheckbox v-model="form.remember" label="保持登录" required /> <VBtn block type="submit" @click="login"> 登录 </VBtn>
</div> <div v-if="errorMessage" class="text-error mt-2 text-shadow">
</VCol> {{ errorMessage }}
<VCol cols="12"> </div>
<!-- login button --> </VCol>
<VBtn block type="submit" @click="login"> 登录 </VBtn> </VRow>
<div v-if="errorMessage" class="text-error mt-2 text-shadow"> </VForm>
{{ errorMessage }} </VCardText>
</div> </VCard>
</VCol> </div>
</VRow>
</VForm>
</VCardText>
</VCard>
</div> </div>
</template> </template>
@@ -322,4 +293,8 @@ onUnmounted(() => {
.v-card-item__prepend { .v-card-item__prepend {
padding-inline-end: 0 !important; padding-inline-end: 0 !important;
} }
.absolute-top-shift {
inset-block-start: calc(-4rem - env(safe-area-inset-top));
}
</style> </style>