feat: 新增统一启动屏组件,优化跨平台加载体验
1. 新增splash-screen全局启动屏组件,适配非APP平台的加载过渡 2. 新增splash-state状态管理模块,统一处理启动屏的显示隐藏、软隐藏时序、兜底超时逻辑 3. 重构App.vue和main.js的加载逻辑,替换原有uni.showLoading方案 4. 在首页和空页面挂载启动屏组件,适配不同页面场景 5. 兼容H5平台降级处理,解决main.js挂载前无法渲染组件的问题
This commit is contained in:
178
components/splash-screen/splash-screen.vue
Normal file
178
components/splash-screen/splash-screen.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<!-- #ifndef APP-PLUS -->
|
||||
<view v-if="visible" class="splash-screen" :class="{ 'splash-screen--leaving': leaving }">
|
||||
<view class="splash-screen__bg"></view>
|
||||
<view class="splash-screen__content">
|
||||
<view class="splash-screen__logo">
|
||||
<view class="splash-screen__logo-inner">S</view>
|
||||
</view>
|
||||
<view class="splash-screen__name">Shop</view>
|
||||
<view class="splash-screen__loader">
|
||||
<view class="splash-screen__dot"></view>
|
||||
<view class="splash-screen__dot"></view>
|
||||
<view class="splash-screen__dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import splashState from '@/common/js/splash-state.js';
|
||||
|
||||
export default {
|
||||
name: 'splash-screen',
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
leaving: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 1) 启动时同步读取状态 (防止 mount 前已经 hide 的情况)
|
||||
if (splashState.isShow()) {
|
||||
this.visible = true;
|
||||
}
|
||||
// 2) 通知 state 模块: 组件已 mount
|
||||
// 如果此时 splashState 有 _pendingHide (软隐藏), 会触发真关
|
||||
splashState.notifyMounted();
|
||||
// 3) 监听后续事件
|
||||
splashState.on('splash:show', () => {
|
||||
this.visible = true;
|
||||
this.leaving = false;
|
||||
});
|
||||
splashState.on('splash:hide', () => {
|
||||
this._leave();
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
splashState.off('splash:show');
|
||||
splashState.off('splash:hide');
|
||||
splashState.notifyUnmounted();
|
||||
if (this._leaveTimer) {
|
||||
clearTimeout(this._leaveTimer);
|
||||
this._leaveTimer = null;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
_leave() {
|
||||
// 触发淡出动画,动画结束再 unmount
|
||||
this.leaving = true;
|
||||
if (this._leaveTimer) clearTimeout(this._leaveTimer);
|
||||
this._leaveTimer = setTimeout(() => {
|
||||
this.visible = false;
|
||||
this.leaving = false;
|
||||
}, 320);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.splash-screen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 99999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s ease;
|
||||
|
||||
&--leaving {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.splash-screen__bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(180deg, #ffffff 0%, #f7f7f7 100%);
|
||||
}
|
||||
|
||||
.splash-screen__content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.splash-screen__logo {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 24rpx;
|
||||
background: #FF0036;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8rpx 24rpx rgba(255, 0, 54, 0.18);
|
||||
animation: splash-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
|
||||
}
|
||||
|
||||
.splash-screen__logo-inner {
|
||||
color: #fff;
|
||||
font-size: 56rpx;
|
||||
font-weight: 700;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.splash-screen__name {
|
||||
margin-top: 28rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.splash-screen__loader {
|
||||
margin-top: 48rpx;
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.splash-screen__dot {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 50%;
|
||||
background: #FF0036;
|
||||
opacity: 0.4;
|
||||
animation: splash-bounce 1.2s ease-in-out infinite;
|
||||
|
||||
&:nth-child(2) {
|
||||
animation-delay: 0.15s;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes splash-pop {
|
||||
0% {
|
||||
transform: scale(0.6);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes splash-bounce {
|
||||
0%, 80%, 100% {
|
||||
transform: scale(0.7);
|
||||
opacity: 0.4;
|
||||
}
|
||||
40% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user