Files
dukang/apps/mini-user/src/lib/nav-bar.ts
T

129 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useMemo } from 'react';
import Taro from '@tarojs/taro';
export type NavBarMetrics = {
/** 状态栏高度(刘海/灵动岛上方) */
statusBarHeight: number;
/** 导航栏总高度 = 状态栏 + 内容区 */
navBarHeight: number;
/** 标题栏内容区高度(与胶囊对齐) */
navContentHeight: number;
/** 右侧留白,避免与微信胶囊按钮重叠 */
navBarPaddingRight: number;
/** 左侧留白,与右侧对称以实现标题视觉居中 */
navBarPaddingLeft: number;
};
const H5_FALLBACK: NavBarMetrics = {
statusBarHeight: 0,
navBarHeight: 56,
navContentHeight: 56,
/** 避开微信内置浏览器右上角 ··· / 设置入口(约一颗胶囊宽) */
navBarPaddingRight: 96,
navBarPaddingLeft: 16,
};
const WEAPP_FALLBACK: NavBarMetrics = {
statusBarHeight: 20,
navBarHeight: 64,
navContentHeight: 44,
navBarPaddingRight: 96,
navBarPaddingLeft: 96,
};
/** 计算小程序自定义导航栏尺寸(对齐微信胶囊按钮) */
export function getNavBarMetrics(): NavBarMetrics {
if (process.env.TARO_ENV === 'h5') {
// H5:在微信浏览器内额外避让右上角菜单;非微信保持较小右侧留白
const inWechat =
typeof navigator !== 'undefined' && /MicroMessenger/i.test(navigator.userAgent || '');
return {
...H5_FALLBACK,
navBarPaddingRight: inWechat ? 96 : 16,
};
}
try {
const win = Taro.getWindowInfo?.() ?? Taro.getSystemInfoSync();
const menu = Taro.getMenuButtonBoundingClientRect();
const statusBarHeight = win.statusBarHeight ?? WEAPP_FALLBACK.statusBarHeight;
const navContentHeight =
menu.height > 0
? (menu.top - statusBarHeight) * 2 + menu.height
: WEAPP_FALLBACK.navContentHeight;
const navBarHeight = statusBarHeight + navContentHeight;
const navBarPaddingRight =
menu.width > 0
? Math.max(win.windowWidth - menu.left + 8, 16)
: WEAPP_FALLBACK.navBarPaddingRight;
return {
statusBarHeight,
navBarHeight,
navContentHeight,
navBarPaddingRight,
navBarPaddingLeft: navBarPaddingRight,
};
} catch {
return WEAPP_FALLBACK;
}
}
export function useNavBarMetrics(): NavBarMetrics {
return useMemo(() => getNavBarMetrics(), []);
}
/** 仅注入 CSS 变量,供 PageShell / sticky 子元素使用(不加 height/padding */
export function pageShellCssVars(metrics: NavBarMetrics): Record<string, string> {
return {
'--nav-bar-height': `${metrics.navBarHeight}px`,
'--nav-content-height': `${metrics.navContentHeight}px`,
'--nav-status-bar-height': `${metrics.statusBarHeight}px`,
'--nav-padding-right': `${metrics.navBarPaddingRight}px`,
'--nav-padding-left': `${metrics.navBarPaddingLeft}px`,
};
}
/** 顶栏自身样式:statusBar padding + 总高 + CSS 变量 */
export function navBarStyle(metrics: NavBarMetrics): Record<string, string | number> {
return {
paddingTop: `${metrics.statusBarHeight}px`,
height: `${metrics.navBarHeight}px`,
...pageShellCssVars(metrics),
};
}
/** Tab 顶栏内容行:左右留白(标题单独全屏居中) */
export function tabNavContentStyle(metrics: NavBarMetrics): Record<string, string | number> {
return {
height: `${metrics.navContentHeight}px`,
paddingLeft: '20px',
paddingRight: `${metrics.navBarPaddingRight}px`,
boxSizing: 'border-box',
};
}
/** 子页/内页顶栏:标题全屏居中,内容区单独留白 */
export function subPageNavBarStyle(metrics: NavBarMetrics): Record<string, string | number> {
const pagePad = 20;
return {
paddingTop: `${metrics.statusBarHeight}px`,
height: `${metrics.navBarHeight}px`,
'--nav-bar-height': `${metrics.navBarHeight}px`,
'--nav-content-height': `${metrics.navContentHeight}px`,
'--nav-status-bar-height': `${metrics.statusBarHeight}px`,
'--nav-padding-left': `${pagePad}px`,
'--nav-padding-right': `${metrics.navBarPaddingRight}px`,
};
}
/** 子页顶栏内容行:左侧页边距 + 右侧避让胶囊 */
export function subPageNavContentStyle(metrics: NavBarMetrics): Record<string, string | number> {
return {
height: `${metrics.navContentHeight}px`,
paddingLeft: '20px',
paddingRight: `${metrics.navBarPaddingRight}px`,
boxSizing: 'border-box',
};
}