useTaskFormSafeArea.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import { computed, type CSSProperties, ref } from 'vue'
  2. import { onUnload } from '@dcloudio/uni-app'
  3. type KeyboardHeightChangeResult = {
  4. height?: number
  5. }
  6. type KeyboardHeightChangeCallback = (result: KeyboardHeightChangeResult) => void
  7. type MiniProgramKeyboardApi = {
  8. onKeyboardHeightChange?: (callback: KeyboardHeightChangeCallback) => void
  9. offKeyboardHeightChange?: (callback: KeyboardHeightChangeCallback) => void
  10. }
  11. type MiniProgramWindowInfo = UniApp.GetWindowInfoResult & {
  12. safeAreaInsets?: {
  13. bottom?: number
  14. }
  15. safeArea?: {
  16. bottom?: number
  17. }
  18. }
  19. const FOOTER_TOP_PADDING_RPX = 20
  20. const FOOTER_BOTTOM_PADDING_RPX = 20
  21. const FOOTER_BUTTON_HEIGHT_RPX = 88
  22. const CONTENT_EXTRA_BOTTOM_RPX = 24
  23. export const useTaskFormSafeArea = () => {
  24. const keyboardApi = uni as unknown as MiniProgramKeyboardApi
  25. const windowWidth = ref(375)
  26. const safeAreaBottom = ref(0)
  27. const keyboardHeight = ref(0)
  28. let keyboardListenerRegistered = false
  29. const rpxToPx = (rpx: number) => {
  30. return (windowWidth.value / 750) * rpx
  31. }
  32. const activeSafeAreaBottom = computed(() => {
  33. return keyboardHeight.value > 0 ? 0 : safeAreaBottom.value
  34. })
  35. const keyboardVisible = computed(() => keyboardHeight.value > 0)
  36. const footerHeightPx = computed(() => {
  37. return (
  38. rpxToPx(FOOTER_TOP_PADDING_RPX + FOOTER_BUTTON_HEIGHT_RPX + FOOTER_BOTTOM_PADDING_RPX) +
  39. activeSafeAreaBottom.value
  40. )
  41. })
  42. const contentStyle = computed<CSSProperties>(() => {
  43. const paddingBottom =
  44. footerHeightPx.value + keyboardHeight.value + rpxToPx(CONTENT_EXTRA_BOTTOM_RPX)
  45. return {
  46. paddingBottom: `${paddingBottom}px`,
  47. }
  48. })
  49. const footerStyle = computed<CSSProperties>(() => {
  50. return {
  51. bottom: `${keyboardHeight.value}px`,
  52. paddingBottom: `${rpxToPx(FOOTER_BOTTOM_PADDING_RPX) + activeSafeAreaBottom.value}px`,
  53. }
  54. })
  55. const initSystemLayout = () => {
  56. const windowInfo = uni.getWindowInfo() as MiniProgramWindowInfo
  57. windowWidth.value = windowInfo.windowWidth || 375
  58. if (typeof windowInfo.safeAreaInsets?.bottom === 'number') {
  59. safeAreaBottom.value = windowInfo.safeAreaInsets.bottom
  60. return
  61. }
  62. if (
  63. typeof windowInfo.safeArea?.bottom === 'number' &&
  64. typeof windowInfo.screenHeight === 'number'
  65. ) {
  66. safeAreaBottom.value = Math.max(windowInfo.screenHeight - windowInfo.safeArea.bottom, 0)
  67. return
  68. }
  69. safeAreaBottom.value = 0
  70. }
  71. const handleKeyboardHeightChange: KeyboardHeightChangeCallback = (result) => {
  72. const height = Number(result.height ?? 0)
  73. keyboardHeight.value = Number.isFinite(height) && height > 0 ? height : 0
  74. }
  75. const initSafeArea = () => {
  76. initSystemLayout()
  77. if (
  78. keyboardListenerRegistered ||
  79. !keyboardApi.onKeyboardHeightChange ||
  80. !keyboardApi.offKeyboardHeightChange
  81. ) {
  82. return
  83. }
  84. keyboardApi.onKeyboardHeightChange(handleKeyboardHeightChange)
  85. keyboardListenerRegistered = true
  86. }
  87. const disposeSafeArea = () => {
  88. if (!keyboardListenerRegistered) {
  89. return
  90. }
  91. keyboardApi.offKeyboardHeightChange?.(handleKeyboardHeightChange)
  92. keyboardListenerRegistered = false
  93. keyboardHeight.value = 0
  94. }
  95. onUnload(disposeSafeArea)
  96. return {
  97. contentStyle,
  98. footerStyle,
  99. keyboardVisible,
  100. initSafeArea,
  101. }
  102. }