safeArea.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { buildSafeAreaCssVarsStyle } from './safeAreaCss.js'
  2. export { buildSafeAreaCssVarsStyle }
  3. const DEFAULT_NAV_CONTENT_HEIGHT = 44
  4. const DEFAULT_GAP = 8
  5. const DEFAULT_HORIZONTAL_PADDING = 8
  6. function getSystemInfo() {
  7. // 微信基础库 3.x 起 getSystemInfoSync 已弃用,优先拆分 API
  8. try {
  9. if (typeof uni.getWindowInfo === 'function') {
  10. const win = uni.getWindowInfo() || {}
  11. return {
  12. statusBarHeight: win.statusBarHeight,
  13. windowWidth: win.windowWidth
  14. }
  15. }
  16. } catch (_) {
  17. /* fall through */
  18. }
  19. try {
  20. return uni.getSystemInfoSync() || {}
  21. } catch (_) {
  22. return {}
  23. }
  24. }
  25. function getMenuButtonRect() {
  26. try {
  27. const rect = uni.getMenuButtonBoundingClientRect?.()
  28. return rect && rect.width ? rect : null
  29. } catch (error) {
  30. return null
  31. }
  32. }
  33. export function computeSafeAreaMetrics() {
  34. const systemInfo = getSystemInfo()
  35. const menuRect = getMenuButtonRect()
  36. const statusBarHeight = Number(systemInfo.statusBarHeight || 20)
  37. const windowWidth = Number(systemInfo.windowWidth || 375)
  38. let navContentHeight = DEFAULT_NAV_CONTENT_HEIGHT
  39. let navTopGap = DEFAULT_GAP
  40. let capsuleWidth = 0
  41. let capsuleRightSafeWidth = 0
  42. if (menuRect) {
  43. navTopGap = Math.max(Number(menuRect.top || 0) - statusBarHeight, DEFAULT_GAP)
  44. navContentHeight = Math.max(Number(menuRect.height || 0) + navTopGap * 2, DEFAULT_NAV_CONTENT_HEIGHT)
  45. capsuleWidth = Number(menuRect.width || 0)
  46. const rightInset = Math.max(windowWidth - Number(menuRect.right || windowWidth), 0)
  47. capsuleRightSafeWidth = Math.max(capsuleWidth + rightInset + DEFAULT_HORIZONTAL_PADDING, 96)
  48. } else {
  49. // 无胶囊位(H5、无自定义导航栏等):不预留右侧占位;样式里已有 32rpx 水平内边距
  50. capsuleRightSafeWidth = 0
  51. }
  52. const topSafeHeight = statusBarHeight
  53. const headerHeight = topSafeHeight + navContentHeight
  54. return {
  55. statusBarHeight,
  56. topSafeHeight,
  57. navContentHeight,
  58. headerHeight,
  59. navTopGap,
  60. capsuleWidth,
  61. capsuleRightSafeWidth,
  62. }
  63. }
  64. export function applySafeAreaCssVars(metrics) {
  65. if (typeof document === 'undefined' || !document?.documentElement) return
  66. const root = document.documentElement.style
  67. const style = buildSafeAreaCssVarsStyle(metrics)
  68. Object.entries(style).forEach(([key, value]) => {
  69. root.setProperty(key, value)
  70. })
  71. }
  72. export function initGlobalSafeArea() {
  73. const metrics = computeSafeAreaMetrics()
  74. const app = getApp?.()
  75. if (app) {
  76. app.globalData = app.globalData || {}
  77. app.globalData.safeArea = metrics
  78. }
  79. applySafeAreaCssVars(metrics)
  80. return metrics
  81. }
  82. export function getGlobalSafeArea() {
  83. const app = getApp?.()
  84. return app?.globalData?.safeArea || computeSafeAreaMetrics()
  85. }
  86. export function getSafeAreaStyle() {
  87. return buildSafeAreaCssVarsStyle(getGlobalSafeArea())
  88. }