国际化您的键盘控制

最近我遇到两个可爱的新的图形演示,在这两种情况下,控制在我的法语 AZERTY 键盘 上无法工作。

一个是精彩的 WebGL 2 技术演示 After The Flood,另一个是超级可爱的 Alpaca Peck。当我告诉 Shaw 这个问题的时候,他很好地修复了后者。事实证明,Web 浏览器实际上公开了一个有用的 API 来解决这个问题。

让我们进一步调查。

一个键盘,多种布局

世界各地的人们使用不同的键盘布局。您可以在 维基百科的键盘布局页面 上阅读很多内容,但我会尝试在这里总结重要的部分。

最著名、使用最广泛的布局是 QWERTY,在世界大部分地区使用

A QWERTY layout. This layout is called QWERTY for the first six letters in the keyboard.

您可能还知道 AZERTY,在一些法语国家使用

A AZERTY layout. AZERTY are the six first letters in the keyboard. Many, but not all of the letters, are in the same place as in QWERTY.

此外,QWERTZ 键盘在德国和其他欧洲国家使用,DVORAK 是 QWERTY 的另一种替代方案

A DVORAK layout. This layout is completely different from AZERTY and QWERTY.

每个布局都有变体,尤其是在最上面的行中的符号以及右手键中。同一个布局系列的两个键盘可能并不完全相同。例如,西班牙 QWERTY 键盘有一个专门用于 ñ 的键,德国 QWERTZ 键盘有用于 äö 的专门键。

A QWERTZ layout is really close to QWERTY yet has subtle differences.

您会注意到,所有布局的键盘在本质上具有相同的结构。在大多数情况下,键位于相同的位置,尽管它们可以稍微重新排列或调整。这称为机械布局

因此,区域布局由以下部分组成

  • 视觉布局在物理键上物理印刷。
  • 功能布局指的是将硬件键映射到字符的软件(驱动程序)。

这意味着**我们实际上可以在不改变物理键盘的情况下更改操作系统中使用的布局**。它们是两个不同的东西!一些用户会安装 改进 布局 驱动程序 以能够更快地输入或更轻松地输入特定字符。当有用的字符通常在布局中不可用时,这非常有用。例如,为了用法语输入,我可以很容易地使用我使用的驱动程序输入 ÉÈÇ 或法语引号 «»

但是,当您需要用多种语言书写文本时,它也很有用:我的键盘上没有 ø 字符,但我的驱动程序允许我轻松地输入它。

在 Web 上发生了什么?

好吧,过去它完全是一团糟。然后我们融合了相当适合 QWERTY 键盘的跨浏览器行为。

我们习惯使用的 API 围绕三个事件展开:keydownkeypresskeyupkeydownkeyup 被称为键事件,因为它们在用户按下任何键时都会触发,而 keypress 被称为字符事件,因为它应该在按下键后发送字符时触发。所有现代浏览器似乎都同意这一点,即使情况并非总是如此。

对于此遗留 API,我们使用 KeyboardEvent 的三个属性:keyCodecharCodewhich。我不会详细介绍这里的内容,请相信我,这对于使用来说是一场噩梦

  • 在处理键事件(keydownkeyup)与字符事件(keypress)时,属性不具有相同的含义。
  • 对于某些键和事件,即使对于最新的浏览器版本,值在跨浏览器方面也不一致。
  • keyCode 在键事件中尝试对国际友好 - 不,真的 - 但由于缺乏通用规范,它惨败。

所以,让我们看看新的 API 带来了哪些改进!

新 API,作为 UI 事件的一部分

UI 事件,以前称为 DOM Level 3 事件,是一个自 2000 年起一直在讨论的 W3C 规范。它仍在作为工作草案进行讨论,但由于大多数浏览器今天似乎都同意,我们可以希望该规范会继续发展成为推荐。最新的 键盘事件工作草案 现在在线提供。

新 API 为 KeyboardEvent 事件带来了两个非常有用的新属性:keycode。它们取代了以前存在的(并且仍然存在)charCodekeyCodewhich

让我们看看为什么这些变化如此有用,尤其是在进行跨键盘网站(如果您允许我使用这个新词语)时。

KeyboardEvent.key 为您提供可打印字符或描述性字符串

属性 key 几乎直接替换了以前使用的 which,只是它更可预测。

当按下的键是可打印字符时,您将获得字符串形式的字符(而不是 whichkeyCode 的 ASCII/Windows-1252 代码或 charCode 的 Unicode 代码)。

当按下的键不是可打印字符(例如:退格键控制键,但也包括回车键制表键,它们实际上可打印字符)时,您会获得一个多字符描述性字符串,例如 'Backspace''Control''Enter''Tab'

在主要的现代桌面浏览器中,只有 Safari 尚未支持该属性,但将在下一个版本中支持。

KeyboardEvent.key Browser Usage, February 2017. All desktop browsers except Safari support the property. Next version of Safari will support it though.

KeyboardEvent.code 为您提供物理键

该属性在此规范中是全新的,尽管它应该是 keyCode 应该成为的样子。

它以字符串形式为您提供按下的物理键。这意味着它完全独立于使用的键盘布局。

因此,假设用户在 QWERTY 键盘上按下Q键。然后 event.code 为您提供 'KeyQ',而 event.key 为您提供 'q'

但是,当 AZERTY 键盘用户按下A键时,他也得到 'KeyQ' 作为 event.code,而 event.key 包含 'a'。这是因为 AZERTY 键盘上的A键与 QWERTY 键盘上的Q键位于相同的位置。

至于数字,顶部的数字键行会产生诸如 'Digit1' 之类的值,而数字小键盘会产生诸如 'Numpad1' 之类的值。

不幸的是,此功能目前仅在 Blink 和 Firefox 中实现,但 Safari 的支持即将到来。

KeyboardEvent.code Browser Usage, February 2017. Firefox and Blink-based browsers like Chrome and Opera support it. Safari will support it in the next version. Internet Explorer, Edge and most mobile browsers do not.

参考键盘

如果每个键都触发一个特定的代码…,那么我能听到您的下一个问题。哪个代码为哪个键触发?参考键盘是什么?

这比看起来更复杂。没有现有的键盘具有所有可能的键。

这就是为什么 W3C 发布了 专门针对此的规范。您可以阅读有关 世界各地现有的机械布局以及它们的参考键盘 的信息。例如,这是他们针对字母数字部分的参考键盘

Keyboard Codes, alphanumeric section. Click for a SVG version

我鼓励您看看并至少了解一下此规范的概况。

另请注意,W3C 还发布了 描述key属性值的兄弟规范

键和代码之间的关系

我强烈建议您通读 规范中给出的示例。它们非常清楚地展示了用户按下各种类型键时发生的情况,包括codekey

跨浏览器控制

很棒的 Mozilla 开发者网络提供了一个很好的示例,说明 如何使用 WASD 或箭头控制游戏。但该示例无法跨浏览器运行,尤其是在 Safari 或 Internet Explorer 上无法运行,因为它们尚未实现该规范。因此,让我们看看如何支持一些跨浏览器代码。

当然,在未实现规范的情况下,它在非 QWERTY 键盘上无法正常运行。因此,最好也使用箭头键,因为它们始终位于相同的位置。在此示例中,我还使用数字小键盘和 IJKL 键,因为它们不太可能位于不同的位置。

以下是如何使用 JavaScript 代码支持新 API 和旧 API 的示例。


window.addEventListener('keydown', function(e) {
  if (e.defaultPrevented) {
    return;
  }

  // We don't want to mess with the browser's shortcuts
  if (e.ctrlKey || e.altKey || e.metaKey || e.shiftKey) {
    return;
  }

  // We try to use `code` first because that's the layout-independent property.
  // Then we use `key` because some browsers, notably Internet Explorer and
  // Edge, support it but not `code`. Then we use `keyCode` to support older
  // browsers like Safari, older Internet Explorer and older Chrome.
  switch (e.code || e.key || e.keyCode) {
    case 'KeyW': // This is 'W' on QWERTY keyboards, but 'Z' on AZERTY keyboards
    case 'KeyI':
    case 'ArrowUp':
    case 'Numpad8':
    case 38: // keyCode for arrow up
      changeDirectionUp();
      break;
   
    // ... Other letters: ASD, JKL, arrows, numpad

    default:
      return;
  }

  e.preventDefault();
  doSomethingUseful();
});

// touch handling
// A real implementation would want to use touchstart and touchend as well.
window.addEventListener('touchmove', function(e) {
  // don't forget to throttle the event
});

尝试完整版本!

缺少什么?

API 本身做得很好,没有太多缺失。

但我还是想念了一些东西。无法知道当前的键盘布局是什么。这对于编写游戏的控制说明非常有用:按下 WASD/ZQSD/...,具体取决于布局。

了解特定键后面哪个字母的 API 也很有用。但是我无法确定底层操作系统是否提供必要的低级调用来提供这些信息。

其他有用的东西

在不赘述的情况下,让我们快速浏览一下 API 中的一些其他重要功能

  • keypress 事件已弃用。现在您应该始终使用 keydown。事件 beforeinput 也已计划,但目前尚未得到任何稳定版本的浏览器支持(Chrome Canary 有一个实现)。事件 input 是所有浏览器都支持的更高级别的事件,在某些情况下也很有用。
  • 使用 KeyboardEvent 上的 location 属性,如果按下的键存在于多个位置(例如 Shift 或 Ctrl 键,或数字),那么您可以知道实际使用了哪个键。例如,您可以知道按下的键是在数字键盘上还是在数字顶部栏上。
    注意:此信息也包含在 code 属性中,因为每个物理键都有自己的 code
  • 如果用户持续按下某个键并因此重复发送事件,则 repeat 属性将设置为 true
  • 如果您想在处理另一个键的 KeyboardEvent 时知道某个修饰键是否被按下,您无需自己跟踪状态。布尔属性 altKeyctrlKeymetaKeyshiftKey 以及方法 getModifierState 可以告诉您事件触发时各种修饰键的状态。

奇怪的是,键盘事件在移动平台上似乎无法正常工作(未测试 iPhone)。因此,请确保也提供触摸界面!

您现在可以使用它

我的结论是:您现在就可以使用它!可以通过利用现代浏览器的较新 API,同时支持较旧的浏览器,逐步增强游戏控制器代码。

您的国际用户会感谢您这样做……通过使用您的产品 :-)

关于 Julien Wajsberg

Julien 多年来一直使用和贡献网络,自 2012 年起就职于 Mozilla。他曾是 Firefox OS 开发者,现在是 Firefox 开发者工具团队的一员。

Julien Wajsberg 的更多文章……


3 条评论

  1. Noitidart

    我期待一个 getKeyFromCode 函数。这样,我们的“热键页面”就可以正确地提供可打印的键名,无论键盘是什么。

    是否有计划为此函数?

    2017 年 3 月 17 日 上午 11:38

    1. Julien Wajsberg

      是的,我在我的“缺失了什么”部分提到了这个需求。我同意您的观点,但据我所知,目前还没有任何计划。但 UI 事件规范尚未完成,可以向工作组提出这些问题。

      2017 年 3 月 17 日 下午 3:47

  2. Tomas Kapler

    我需要提醒开发人员,国际键盘有自己的复杂快捷键来书写标准键盘上没有的某些字母。例如,对于捷克键盘,我们没有 [ ] 字符,您必须输入右 Alt + F 来输入 [,右 Alt + G 来输入 ]。而右 Alt 在这里与左 Alt + CTRL 相同。因此,例如,当 WordPress 在编辑器中添加了一个新的键盘快捷键 CTRL+ALT+F 来执行某些操作时,我们无法输入 [ 字符(这对短代码来说非常重要,类似于小部件)。
    因此,如果您确实要创建一些键盘快捷键,请同时考虑非美国键盘用户的需求,并最好允许在您的语言文件中或通过某些管理界面重新编程它。

    2017 年 3 月 24 日 上午 8:55

本文的评论已关闭。