Files
mini-programs/src/store/keyboardStore.ts
2025-11-16 20:06:22 +08:00

162 lines
4.4 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 { 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
}
}