Skip to content

useMagicKeys

响应式按键按下状态,支持神奇按键组合。

使用方法

ts
import { useMagicKeys } from '@vueuse/core'

const { shift, space, a /* 要监听的按键 */ } = useMagicKeys()

watch(space, (v) => {
  if (v)
    console.log('空格键被按下')
})

watchEffect(() => {
  if (shift.value && a.value)
    console.log('Shift + A 已经被按下')
})

注意

如果你在 tsconfig.json 中启用了 TypeScript 的 noUncheckedIndexedAccess 选项(或者使用默认启用该选项的 Nuxt),那么解构出来的按键的类型将是 ComputedRef<boolean> | undefined

noUncheckedIndexedAccess 选项会为通过索引签名访问的未声明字段加上 undefined 类型。由于 useMagicKeys() 使用索引签名来动态访问任意按键,因此 TypeScript 会为安全性将解构属性视作可能为 undefined。

你需要使用可选链或者通过 getter 函数包裹:

ts
const { shift, space, a } = useMagicKeys()

watch(
  () => space?.value,
  (v) => {
    if (v)
      console.log('space 被按下了')
  },
)

watchEffect(() => {
  if (shift?.value && a?.value)
    console.log('Shift + A 被按下了')
})

更多关于 noUncheckedIndexedAccess 的详情请参阅 TypeScript 文档

查看 所有可能的按键码

组合键

你可以通过使用 +_ 将按键连接起来,以使用组合键(快捷键/热键)。

ts
import { useMagicKeys } from '@vueuse/core'

const keys = useMagicKeys()
const shiftCtrlA = keys['Shift+Ctrl+A']

watch(shiftCtrlA, (v) => {
  if (v)
    console.log('Shift + Ctrl + A 已经被按下')
})
ts
import { useMagicKeys } from '@vueuse/core'

const { Ctrl_A_B, space, alt_s /* ... */ } = useMagicKeys()

watch(Ctrl_A_B, (v) => {
  if (v)
    console.log('Control+A+B 已经被按下')
})

你还可以使用 whenever 函数让代码更加简洁

ts
import { useMagicKeys, whenever } from '@vueuse/core'

const keys = useMagicKeys()

whenever(keys.shift_space, () => {
  console.log('Shift+Space 已经被按下')
})

当前按下的键

提供了一个特殊的属性 current,用于表示当前按下的所有键。

ts
import { useMagicKeys, whenever } from '@vueuse/core'

const { current } = useMagicKeys()

console.log(current) // Set { 'control', 'a' }

whenever(
  () => current.has('a') && !current.has('b'),
  () => console.log('A 键被按下,但 B 键没有被按下'),
)

按键别名

ts
import { useMagicKeys, whenever } from '@vueuse/core'

const { shift_cool } = useMagicKeys({
  aliasMap: {
    cool: 'space',
  },
})

whenever(shift_cool, () => console.log('Shift + Space 已经被按下'))

默认情况下,我们提供了一些用于常见做法的预配置别名

有条件地禁用

你可能在应用中有一些 <input /> 元素,当用户聚焦于这些输入框时,你不希望触发神奇按键处理。这里是一个使用 useActiveElementlogicAnd 的示例来实现这一点:

ts
import { useActiveElement, useMagicKeys, whenever } from '@vueuse/core'
import { logicAnd } from '@vueuse/math'

const activeElement = useActiveElement()
const notUsingInput = computed(() =>
  activeElement.value?.tagName !== 'INPUT'
  && activeElement.value?.tagName !== 'TEXTAREA',
)

const { tab } = useMagicKeys()

whenever(logicAnd(tab, notUsingInput), () => {
  console.log('Tab 键被按下,且不在输入框中!')
})

自定义事件处理程序

ts
import { useMagicKeys, whenever } from '@vueuse/core'

const { ctrl_s } = useMagicKeys({
  passive: false,
  onEventFired(e) {
    if (e.ctrlKey && e.key === 's' && e.type === 'keydown')
      e.preventDefault()
  },
})

whenever(ctrl_s, () => console.log('Ctrl+S 已经被按下'))

⚠️ 不建议使用此用法,请谨慎操作。

响应式模式

默认情况下,useMagicKeys() 返回的是 Ref<boolean> 类型。如果你想在模板中直接使用对象,可以启用响应式模式。

ts
import { useMagicKeys } from '@vueuse/core'
// ---cut---
const keys = useMagicKeys({ reactive: true })
vue
<template>
  <div v-if="keys.shift">
    你按住了 Shift 键!
  </div>
</template>

Type Declarations

ts
export interface UseMagicKeysOptions<Reactive extends boolean> {
  /**
   * 返回一个响应式对象而不是 ref 对象
   *
   * @default false
   */
  reactive?: Reactive
  /**
   * 监听事件的目标
   *
   * @default window
   */
  target?: MaybeRefOrGetter<EventTarget>
  /**
   * 键的别名映射,所有键都应为小写
   * { target: keycode }
   *
   * @example { ctrl: "control" }
   * @default <预定义映射>
   */
  aliasMap?: Record<string, string>
  /**
   * 注册被动监听器
   *
   * @default true
   */
  passive?: boolean
  /**
   * 自定义键盘按下/释放事件的处理程序。
   * 当你想应用自定义逻辑时很有用。
   *
   * 当使用 `e.preventDefault()` 时,你需要传递 `passive: false` 给 `useMagicKeys()`。
   */
  onEventFired?: (e: KeyboardEvent) => void | boolean
}
export interface MagicKeysInternal {
  /**
   * 当前按下的键的集合,
   * 存储原始的键码。
   *
   * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
   */
  current: Set<string>
}
export type UseMagicKeysReturn<Reactive extends boolean> = Readonly<
  Record<string, Reactive extends true ? boolean : ComputedRef<boolean>> &
    MagicKeysInternal
>
/**
 * 响应式按键按下状态,具有神奇按键组合支持。
 *
 * @see /useMagicKeys
 */
export declare function useMagicKeys<T extends boolean = false>(
  options?: UseMagicKeysOptions<T>,
): UseMagicKeysReturn<T>
export { DefaultMagicKeysAliasMap } from "./aliasMap"