162 lines
4.4 KiB
TypeScript
162 lines
4.4 KiB
TypeScript
import { create } from 'zustand'
|
||
import Taro from '@tarojs/taro'
|
||
|
||
interface KeyboardState {
|
||
keyboardHeight: number
|
||
isKeyboardVisible: boolean
|
||
listeners: Set<(height: number, visible: boolean) => void>
|
||
isInitialized: boolean
|
||
}
|
||
|
||
interface KeyboardActions {
|
||
setKeyboardHeight: (height: number) => void
|
||
setKeyboardVisible: (visible: boolean) => void
|
||
addListener: (listener: (height: number, visible: boolean) => void) => () => void
|
||
initializeKeyboardListener: () => void
|
||
cleanup: () => void
|
||
}
|
||
|
||
type KeyboardStore = KeyboardState & KeyboardActions
|
||
|
||
export const useKeyboardStore = create<KeyboardStore>((set, get) => ({
|
||
keyboardHeight: 0,
|
||
isKeyboardVisible: false,
|
||
listeners: new Set(),
|
||
isInitialized: false,
|
||
|
||
setKeyboardHeight: (height: number) => {
|
||
// 直接更新状态,不触发监听器(监听器由 initializeKeyboardListener 统一处理)
|
||
set({ keyboardHeight: height })
|
||
},
|
||
|
||
setKeyboardVisible: (visible: boolean) => {
|
||
// 直接更新状态,不触发监听器(监听器由 initializeKeyboardListener 统一处理)
|
||
set({ isKeyboardVisible: visible })
|
||
},
|
||
|
||
addListener: (listener: (height: number, visible: boolean) => void) => {
|
||
const { listeners } = get()
|
||
listeners.add(listener)
|
||
|
||
// 返回取消监听的函数
|
||
return () => {
|
||
listeners.delete(listener)
|
||
}
|
||
},
|
||
|
||
initializeKeyboardListener: () => {
|
||
const { isInitialized } = get()
|
||
if (isInitialized) return
|
||
|
||
console.log('初始化全局键盘监听器')
|
||
|
||
// 使用防抖优化,避免频繁触发
|
||
let lastHeight = 0
|
||
let lastTime = 0
|
||
let pendingUpdate: { height: number; visible: boolean } | null = null
|
||
let rafId: number | null = null
|
||
const debounceDelay = 50 // 增加到50ms,减少更新频率
|
||
|
||
const applyUpdate = () => {
|
||
if (!pendingUpdate) return
|
||
|
||
const { height, visible } = pendingUpdate
|
||
const store = get()
|
||
|
||
// 批量更新,减少状态更新次数
|
||
if (visible) {
|
||
set({
|
||
isKeyboardVisible: true,
|
||
keyboardHeight: height
|
||
})
|
||
// 异步通知监听器
|
||
requestAnimationFrame(() => {
|
||
const { listeners } = get()
|
||
listeners.forEach(listener => {
|
||
try {
|
||
listener(height, true)
|
||
} catch (error) {
|
||
console.error('键盘监听器执行错误:', error)
|
||
}
|
||
})
|
||
})
|
||
} else {
|
||
set({
|
||
isKeyboardVisible: false,
|
||
keyboardHeight: 0
|
||
})
|
||
// 异步通知监听器
|
||
requestAnimationFrame(() => {
|
||
const { listeners } = get()
|
||
listeners.forEach(listener => {
|
||
try {
|
||
listener(0, false)
|
||
} catch (error) {
|
||
console.error('键盘监听器执行错误:', error)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
pendingUpdate = null
|
||
rafId = null
|
||
}
|
||
|
||
Taro.onKeyboardHeightChange?.((res: any) => {
|
||
const height = Number(res?.height || 0)
|
||
const now = Date.now()
|
||
const visible = height > 0
|
||
|
||
// 防抖:如果高度没变化或时间间隔太短,忽略
|
||
if (height === lastHeight && (now - lastTime) < debounceDelay) {
|
||
return
|
||
}
|
||
|
||
lastHeight = height
|
||
lastTime = now
|
||
|
||
// 保存待更新的状态
|
||
pendingUpdate = { height, visible }
|
||
|
||
// 取消之前的更新
|
||
if (rafId !== null) {
|
||
cancelAnimationFrame(rafId)
|
||
}
|
||
|
||
// 延迟执行更新,避免阻塞消息处理
|
||
rafId = requestAnimationFrame(() => {
|
||
requestAnimationFrame(applyUpdate)
|
||
})
|
||
})
|
||
|
||
set({ isInitialized: true })
|
||
},
|
||
|
||
cleanup: () => {
|
||
console.log('清理全局键盘监听器')
|
||
// @ts-ignore
|
||
if (typeof Taro.offKeyboardHeightChange === 'function') {
|
||
// @ts-ignore
|
||
Taro.offKeyboardHeightChange()
|
||
}
|
||
set({
|
||
isInitialized: false,
|
||
keyboardHeight: 0,
|
||
isKeyboardVisible: false,
|
||
listeners: new Set()
|
||
})
|
||
}
|
||
}))
|
||
|
||
// 导出便捷的 hooks
|
||
export const useKeyboardHeight = () => {
|
||
const { keyboardHeight, isKeyboardVisible, addListener, initializeKeyboardListener } = useKeyboardStore()
|
||
|
||
return {
|
||
keyboardHeight,
|
||
isKeyboardVisible,
|
||
addListener,
|
||
initializeKeyboardListener
|
||
}
|
||
}
|