fix iOS tabbar transition
This commit is contained in:
@@ -23,7 +23,8 @@
|
|||||||
<page-transition />
|
<page-transition />
|
||||||
</div>
|
</div>
|
||||||
<t-tab-bar
|
<t-tab-bar
|
||||||
:class="{ 'is-hidden': !tabBarStore.isShowing }"
|
<!-- iOS 手势返回场景下临时关闭 tabbar 过渡,避免系统回退与页面动画叠加 -->
|
||||||
|
:class="{ 'is-hidden': !tabBarStore.isShowing, 'skip-transition': tabBarStore.shouldSkipTransition }"
|
||||||
class="tab-bar glass-white"
|
class="tab-bar glass-white"
|
||||||
v-model="tabVal"
|
v-model="tabVal"
|
||||||
shape="round"
|
shape="round"
|
||||||
@@ -261,6 +262,11 @@ const contentHeight = computed(() => {
|
|||||||
transform: translateY(calc(100% + 1rem));
|
transform: translateY(calc(100% + 1rem));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.skip-transition {
|
||||||
|
// 配合 tabBarStore.shouldSkipTransition:仅本次导航禁用过渡
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ import ServerPerformanceDetail from "@/pages/dashboard/ServerDashboard/ServerPer
|
|||||||
import ServerPartitionsDetail from "@/pages/dashboard/ServerDashboard/ServerPartitionsDetail.vue";
|
import ServerPartitionsDetail from "@/pages/dashboard/ServerDashboard/ServerPartitionsDetail.vue";
|
||||||
import DockerContainerDetail from "@/pages/dashboard/DockerDashboard/DockerContainerDetail.vue";
|
import DockerContainerDetail from "@/pages/dashboard/DockerDashboard/DockerContainerDetail.vue";
|
||||||
import {
|
import {
|
||||||
|
hasProgrammaticBackNavigation,
|
||||||
clearProgrammaticBackNavigation,
|
clearProgrammaticBackNavigation,
|
||||||
markProgrammaticBackNavigation
|
markProgrammaticBackNavigation
|
||||||
} from "@/utils/backNavigationSignal";
|
} from "@/utils/backNavigationSignal";
|
||||||
|
import { useTabBarStore } from "@/store/tabBarStore";
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory("/"),
|
history: createWebHistory("/"),
|
||||||
@@ -186,6 +188,29 @@ const router = createRouter({
|
|||||||
|
|
||||||
const rawBack = router.back.bind(router);
|
const rawBack = router.back.bind(router);
|
||||||
const rawGo = router.go.bind(router);
|
const rawGo = router.go.bind(router);
|
||||||
|
const isIOS = /iP(ad|hone|od)/i.test(window.navigator.userAgent);
|
||||||
|
// 与 MainLayout 的 tabbar 过渡时长保持一致,额外预留一点缓冲时间
|
||||||
|
const TAB_BAR_TRANSITION_MS = 520;
|
||||||
|
const TAB_BAR_SKIP_EXTRA_MS = 180;
|
||||||
|
const depthCache = new Map<string, number>();
|
||||||
|
|
||||||
|
function calcDepth(sourceRoute: RouteLocationNormalized): number {
|
||||||
|
if (!sourceRoute.meta.dynamicDepth) {
|
||||||
|
return Number(sourceRoute.meta.depth ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullPath = sourceRoute.fullPath;
|
||||||
|
const cachedDepth = depthCache.get(fullPath);
|
||||||
|
if (cachedDepth !== undefined) {
|
||||||
|
return cachedDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseDepth = Number(sourceRoute.meta.baseDepth ?? 0);
|
||||||
|
const pathSegments = fullPath.split("?")[0].split("/").filter((pathSegment) => pathSegment !== "");
|
||||||
|
const routeDepth = baseDepth + (pathSegments.length - 1);
|
||||||
|
depthCache.set(fullPath, routeDepth);
|
||||||
|
return routeDepth;
|
||||||
|
}
|
||||||
|
|
||||||
// 全局代理:所有代码触发的后退都自动标记为“主动返回”。
|
// 全局代理:所有代码触发的后退都自动标记为“主动返回”。
|
||||||
router.back = () => {
|
router.back = () => {
|
||||||
@@ -201,8 +226,20 @@ router.go = (delta) => {
|
|||||||
rawGo(delta);
|
rawGo(delta);
|
||||||
};
|
};
|
||||||
|
|
||||||
router.beforeEach((to: RouteLocationNormalized) => {
|
router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalized) => {
|
||||||
|
const tabBarStore = useTabBarStore();
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
|
const toDepth = calcDepth(to);
|
||||||
|
const fromDepth = calcDepth(from);
|
||||||
|
const isBackwardNavigation = toDepth < fromDepth;
|
||||||
|
// 代码主动调用 back/go(-1) 的返回,仍保留 tabbar 进入动画
|
||||||
|
const isProgrammaticBack = hasProgrammaticBackNavigation();
|
||||||
|
const isTabBarEntering = !from.meta.tabBarVisible && !!to.meta.tabBarVisible;
|
||||||
|
// 仅 iOS 手势返回(非代码触发)且无 tabbar -> 有 tabbar 时,禁用本次 tabbar 进入动画
|
||||||
|
const shouldSkipTabBarEnterTransition = isIOS && isBackwardNavigation && !isProgrammaticBack && isTabBarEntering;
|
||||||
|
if (shouldSkipTabBarEnterTransition) {
|
||||||
|
tabBarStore.skipTransitionOnce();
|
||||||
|
}
|
||||||
|
|
||||||
if (to.meta.ignoreConnectCheck) {
|
if (to.meta.ignoreConnectCheck) {
|
||||||
return true;
|
return true;
|
||||||
@@ -221,6 +258,13 @@ router.beforeEach((to: RouteLocationNormalized) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.afterEach((to: RouteLocationNormalized) => {
|
router.afterEach((to: RouteLocationNormalized) => {
|
||||||
|
const tabBarStore = useTabBarStore();
|
||||||
|
if (tabBarStore.shouldSkipTransition) {
|
||||||
|
// 本次导航稳定后再恢复过渡,避免手势返回期间出现二次进入动画
|
||||||
|
window.setTimeout(() => {
|
||||||
|
tabBarStore.restoreTransition();
|
||||||
|
}, TAB_BAR_TRANSITION_MS + TAB_BAR_SKIP_EXTRA_MS);
|
||||||
|
}
|
||||||
// 一次导航结束后清理信号,防止影响后续动画判定。
|
// 一次导航结束后清理信号,防止影响后续动画判定。
|
||||||
clearProgrammaticBackNavigation();
|
clearProgrammaticBackNavigation();
|
||||||
const globalUIStore = useGlobalUIStore();
|
const globalUIStore = useGlobalUIStore();
|
||||||
|
|||||||
@@ -2,10 +2,23 @@ import { defineStore } from "pinia";
|
|||||||
|
|
||||||
export const useTabBarStore = defineStore("tab-bar", () => {
|
export const useTabBarStore = defineStore("tab-bar", () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
// 仅用于“当前这一次”禁用 tabbar 过渡,导航结束后会恢复
|
||||||
|
const shouldSkipTransition = ref(false);
|
||||||
|
|
||||||
const isShowing = computed(() => !!router.currentRoute.value.meta.tabBarVisible);
|
const isShowing = computed(() => !!router.currentRoute.value.meta.tabBarVisible);
|
||||||
|
|
||||||
|
function skipTransitionOnce(): void {
|
||||||
|
shouldSkipTransition.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreTransition(): void {
|
||||||
|
shouldSkipTransition.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isShowing
|
isShowing,
|
||||||
|
shouldSkipTransition,
|
||||||
|
skipTransitionOnce,
|
||||||
|
restoreTransition
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user