LAOP-mini/src/components/CustomNavBar/index.tsx
2026-09-18 16:54:23 +08:00

98 lines
3.6 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 React, { useMemo } from 'react'
import Taro from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import styles from './index.module.scss'
/**
* 运行时导航栏尺寸(px,需内联样式动态应用)。
*/
export interface NavBarMetrics {
/** 状态栏高度(px) */
statusBarHeight: number
/** 导航内容区高度(px),即胶囊按钮所在行的高度 */
navContentHeight: number
/** 状态栏 + 内容区总高度(px),页面顶部留白直接使用该值 */
totalHeight: number
}
/**
* 获取自定义导航栏运行时尺寸。
*
* <p>计算规则:内容区高度 = (胶囊顶部 - 状态栏高度) × 2 + 胶囊高度,
* 保证标题与右上角胶囊按钮垂直居中对齐;非微信端(H5 预览等)
* 胶囊 API 不可用时降级为 statusBar 20px + content 44px 的通用值。</p>
*/
export function useNavBarMetrics(): NavBarMetrics {
return useMemo(() => {
let statusBarHeight = 20
let navContentHeight = 44
try {
const sysInfo = Taro.getSystemInfoSync()
statusBarHeight = sysInfo.statusBarHeight || 20
if (typeof Taro.getMenuButtonBoundingClientRect === 'function') {
const rect = Taro.getMenuButtonBoundingClientRect()
if (rect && rect.top > 0 && rect.height > 0) {
navContentHeight = (rect.top - statusBarHeight) * 2 + rect.height
}
}
} catch (err) {
console.warn('[CustomNavBar] 获取系统信息失败,使用默认导航高度:', err)
}
return { statusBarHeight, navContentHeight, totalHeight: statusBarHeight + navContentHeight }
}, [])
}
interface CustomNavBarProps {
/** 导航栏标题 */
title: string
/** 页面滚动后显示底色与阴影(透明 → 主题色),由页面根据滚动位置传入 */
solid?: boolean
/** 是否显示返回按钮(非 tabBar 子页传入 true);默认 false */
showBack?: boolean
}
/**
* 自定义导航栏(沉浸式)。
*
* <p><b>为什么需要自定义:</b>原生导航栏与页面渐变背景之间存在硬分界线,
* 无法实现"渐变从状态栏一气呵成"的沉浸式视觉效果;自定义导航栏透明悬浮于
* 页面渐变之上,滚动后切换为主题色底 + 阴影,保证标题可读性。</p>
*
* <p><b>使用约定:</b>页面需设置 {@code navigationStyle: 'custom'},
* 并通过 {@link useNavBarMetrics} 将页面顶部内容让出导航高度,
* 防止标题遮挡内容。子页面(非 tabBar 页)可开启 {@code showBack} 显示返回按钮。</p>
*
* @author haisen
*/
const CustomNavBar: React.FC<CustomNavBarProps> = ({ title, solid = false, showBack = false }) => {
const { statusBarHeight, navContentHeight } = useNavBarMetrics()
const handleBack = () => {
// 优先返回上一页,无历史时兜底跳首页
const pages = Taro.getCurrentPages()
if (pages.length > 1) {
Taro.navigateBack()
} else {
Taro.switchTab({ url: '/pages/home/index' })
}
}
return (
<View className={`${styles.navBar} ${solid ? styles.navBarSolid : ''}`}>
{/* 状态栏占位:高度因机型而异,运行时动态获取 */}
<View style={{ height: `${statusBarHeight}px` }} />
{/* 导航内容区:标题与胶囊按钮垂直居中对齐 */}
<View className={styles.navContent} style={{ height: `${navContentHeight}px` }}>
{showBack && (
<View className={styles.backBtn} onClick={handleBack}>
<Text className={styles.backArrow}>‹</Text>
</View>
)}
<Text className={styles.navTitle}>{title}</Text>
</View>
</View>
)
}
export default CustomNavBar