fix: stabilize iOS Safari mobile navigation

This commit is contained in:
jxxghp
2026-06-16 14:20:12 +08:00
parent 18566c0e9d
commit 405e460ad6
5 changed files with 32 additions and 13 deletions

View File

@@ -170,6 +170,10 @@ export default defineComponent({
}
.layout-wrapper.layout-nav-type-vertical {
--layout-navbar-block-size: calc(
env(safe-area-inset-top, 0px) + #{variables.$layout-vertical-nav-navbar-height} + var(--navbar-tab-height)
);
// TODO(v2): Check why we need height in vertical nav & min-height in horizontal nav
min-block-size: 100%;
@@ -185,13 +189,16 @@ export default defineComponent({
.layout-navbar {
position: fixed;
z-index: variables.$layout-vertical-nav-layout-navbar-z-index;
// iOS Safari 在地址栏收起和惯性滚动时可能把 fixed 顶栏和页面滚动层合成到一起,
// 单独提升顶栏图层可避免导航栏短暂上移到安全区下方。
backface-visibility: hidden;
block-size: var(--layout-navbar-block-size);
inline-size: calc(100vw - variables.$layout-vertical-nav-width - 0.5rem);
inset-block-start: 0;
transform: translate3d(0, 0, 0);
.navbar-content-container {
block-size: calc(
env(safe-area-inset-top) + variables.$layout-vertical-nav-navbar-height + var(--navbar-tab-height)
);
block-size: var(--layout-navbar-block-size);
}
@at-root {

View File

@@ -15,7 +15,7 @@ body {
background: rgb(var(--v-theme-background));
overscroll-behavior-y: contain;
--webkit-overflow-scrolling: touch;
-webkit-overflow-scrolling: touch;
}
body,

View File

@@ -1,6 +1,6 @@
import { ref, computed, onMounted } from 'vue'
import { useDisplay } from 'vuetify'
import { checkPWAStatus, isPWADisplayMode } from '@/@core/utils/navigator'
import { checkPWAStatus, isMobileDevice, isPWADisplayMode } from '@/@core/utils/navigator'
// 全局PWA状态确保只初始化一次
const globalPwaStatus = ref<{
@@ -34,11 +34,14 @@ async function initializePWAGlobally() {
globalPwaStatus.value = await checkPWAStatus()
} catch (error) {
console.error('Failed to detect PWA status', error)
const isStandaloneMode = isPWADisplayMode()
// 即使检测失败,也设置一个合理的默认值
globalPwaStatus.value = {
hasPWAFeatures: false,
isStandaloneMode: isPWADisplayMode(),
isPWAEnvironment: isPWADisplayMode(),
isStandaloneMode,
// iOS Safari 浏览器模式可能取不到 Service Worker 注册信息,但移动端仍应使用 App 交互。
isPWAEnvironment: isStandaloneMode || isMobileDevice(),
isFullPWA: false,
}
} finally {
@@ -56,7 +59,8 @@ export function usePWA() {
// 基于新的PWA状态结构
const pwaMode = computed(() => {
return globalPwaStatus.value?.isPWAEnvironment ?? false
// PWA 状态异步恢复前先用移动端特征兜底,避免 Safari 浏览器首屏阶段缺少移动端交互。
return globalPwaStatus.value?.isPWAEnvironment ?? isMobileDevice()
})
const appMode = computed(() => {

View File

@@ -85,7 +85,10 @@ export function usePullDownGesture(options: PullDownOptions = {}) {
})
const indicatorTransform = computed(() => {
return `translate(-50%, ${Math.min(60 + pullDistance.value - config.SHOW_INDICATOR, 70)}px)`
// 顶部基准位置由布局 CSS 负责,这里只让指示器跟随下拉手势轻微移动。
const followOffset = Math.min(Math.max(pullDistance.value - config.SHOW_INDICATOR, 0), 16)
return `translate3d(-50%, ${followOffset}px, 0)`
})
// 弹窗检测函数

View File

@@ -452,6 +452,7 @@ onMounted(async () => {
v-if="appMode && showPullIndicator"
class="pull-indicator"
:style="{
'--pull-indicator-navbar-extra-height': navbarExtraHeight,
opacity: indicatorOpacity,
transform: indicatorTransform,
}"
@@ -475,7 +476,7 @@ onMounted(async () => {
<!-- 👉 Navbar -->
<template #navbar="{ toggleVerticalOverlayNavActive }">
<div
class="theme-navbar-row d-flex h-14 align-center mx-1"
class="theme-navbar-row d-flex h-full align-center mx-1"
:class="{ 'theme-navbar-row--horizontal': showHorizontalThemeNav }"
>
<RouterLink v-if="showHorizontalThemeNav" :to="canAdmin ? '/dashboard' : '/apps'" class="theme-horizontal-logo">
@@ -801,6 +802,7 @@ onMounted(async () => {
.pull-indicator {
position: fixed;
z-index: 20;
display: flex;
align-items: center;
justify-content: center;
@@ -809,11 +811,14 @@ onMounted(async () => {
backdrop-filter: blur(20px);
background: rgba(var(--v-theme-surface), 0.3);
box-shadow: 0 1px 2px rgba(0, 0, 0, 10%), 0 1px 3px rgba(0, 0, 0, 6%);
inset-block-start: 80px;
inset-block-start: calc(
env(safe-area-inset-top, 0px) + 4rem + var(--pull-indicator-navbar-extra-height, 0rem) + 0.75rem
);
inset-inline-start: 50%;
pointer-events: none;
transform: translateX(-50%);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transform: translate3d(-50%, 0, 0);
transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
will-change: opacity, transform;
}
.indicator-icon {