最近我遇到两个可爱的新的图形演示,在这两种情况下,控制在我的法语 AZERTY 键盘 上无法工作。
一个是精彩的 WebGL 2 技术演示 After The Flood,另一个是超级可爱的 Alpaca Peck。当我告诉 Shaw 这个问题的时候,他很好地修复了后者。事实证明,Web 浏览器实际上公开了一个有用的 API 来解决这个问题。
让我们进一步调查。
一个键盘,多种布局
世界各地的人们使用不同的键盘布局。您可以在 维基百科的键盘布局页面 上阅读很多内容,但我会尝试在这里总结重要的部分。
最著名、使用最广泛的布局是 QWERTY,在世界大部分地区使用
您可能还知道 AZERTY,在一些法语国家使用
此外,QWERTZ 键盘在德国和其他欧洲国家使用,DVORAK 是 QWERTY 的另一种替代方案
每个布局都有变体,尤其是在最上面的行中的符号以及右手键中。同一个布局系列的两个键盘可能并不完全相同。例如,西班牙 QWERTY 键盘有一个专门用于 ñ
的键,德国 QWERTZ 键盘有用于 ä
和 ö
的专门键。
您会注意到,所有布局的键盘在本质上具有相同的结构。在大多数情况下,键位于相同的位置,尽管它们可以稍微重新排列或调整。这称为机械布局。
因此,区域布局由以下部分组成
- 视觉布局在物理键上物理印刷。
- 功能布局指的是将硬件键映射到字符的软件(驱动程序)。
这意味着**我们实际上可以在不改变物理键盘的情况下更改操作系统中使用的布局**。它们是两个不同的东西!一些用户会安装 改进 布局 驱动程序 以能够更快地输入或更轻松地输入特定字符。当有用的字符通常在布局中不可用时,这非常有用。例如,为了用法语输入,我可以很容易地使用我使用的驱动程序输入 É
、È
、Ç
或法语引号 «
和 »
。
但是,当您需要用多种语言书写文本时,它也很有用:我的键盘上没有 ø
字符,但我的驱动程序允许我轻松地输入它。
在 Web 上发生了什么?
好吧,过去它完全是一团糟。然后我们融合了相当适合 QWERTY 键盘的跨浏览器行为。
我们习惯使用的 API 围绕三个事件展开:keydown
、keypress
和 keyup
。keydown
和 keyup
被称为键事件,因为它们在用户按下任何键时都会触发,而 keypress
被称为字符事件,因为它应该在按下键后发送字符时触发。所有现代浏览器似乎都同意这一点,即使情况并非总是如此。
对于此遗留 API,我们使用 KeyboardEvent
的三个属性:keyCode
、charCode
和 which
。我不会详细介绍这里的内容,请相信我,这对于使用来说是一场噩梦
- 在处理键事件(
keydown
或keyup
)与字符事件(keypress
)时,属性不具有相同的含义。 - 对于某些键和事件,即使对于最新的浏览器版本,值在跨浏览器方面也不一致。
keyCode
在键事件中尝试对国际友好 - 不,真的 - 但由于缺乏通用规范,它惨败。
所以,让我们看看新的 API 带来了哪些改进!
新 API,作为 UI 事件的一部分
UI 事件,以前称为 DOM Level 3 事件,是一个自 2000 年起一直在讨论的 W3C 规范。它仍在作为工作草案进行讨论,但由于大多数浏览器今天似乎都同意,我们可以希望该规范会继续发展成为推荐。最新的 键盘事件工作草案 现在在线提供。
新 API 为 KeyboardEvent
事件带来了两个非常有用的新属性:key
和 code
。它们取代了以前存在的(并且仍然存在)charCode
、keyCode
和 which
。
让我们看看为什么这些变化如此有用,尤其是在进行跨键盘网站(如果您允许我使用这个新词语)时。
KeyboardEvent.key
为您提供可打印字符或描述性字符串
属性 key
几乎直接替换了以前使用的 which
,只是它更可预测。
当按下的键是可打印字符时,您将获得字符串形式的字符(而不是 which
和 keyCode
的 ASCII/Windows-1252 代码或 charCode
的 Unicode 代码)。
当按下的键不是可打印字符(例如:退格键、控制键,但也包括回车键或制表键,它们实际上是可打印字符)时,您会获得一个多字符描述性字符串,例如 'Backspace'
、'Control'
、'Enter'
、'Tab'
。
在主要的现代桌面浏览器中,只有 Safari 尚未支持该属性,但将在下一个版本中支持。
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 的支持即将到来。
参考键盘
如果每个键都触发一个特定的代码…,那么我能听到您的下一个问题。哪个代码为哪个键触发?参考键盘是什么?
这比看起来更复杂。没有现有的键盘具有所有可能的键。
这就是为什么 W3C 发布了 专门针对此的规范。您可以阅读有关 世界各地现有的机械布局、以及它们的参考键盘 的信息。例如,这是他们针对字母数字部分的参考键盘
我鼓励您看看并至少了解一下此规范的概况。
另请注意,W3C 还发布了 描述key
属性值的兄弟规范。
键和代码之间的关系
我强烈建议您通读 规范中给出的示例。它们非常清楚地展示了用户按下各种类型键时发生的情况,包括code
和key
。
跨浏览器控制
很棒的 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
时知道某个修饰键是否被按下,您无需自己跟踪状态。布尔属性altKey
、ctrlKey
、metaKey
、shiftKey
以及方法 getModifierState 可以告诉您事件触发时各种修饰键的状态。
奇怪的是,键盘事件在移动平台上似乎无法正常工作(未测试 iPhone)。因此,请确保也提供触摸界面!
您现在可以使用它
我的结论是:您现在就可以使用它!可以通过利用现代浏览器的较新 API,同时支持较旧的浏览器,逐步增强游戏控制器代码。
您的国际用户会感谢您这样做……通过使用您的产品 :-)
关于 Julien Wajsberg
Julien 多年来一直使用和贡献网络,自 2012 年起就职于 Mozilla。他曾是 Firefox OS 开发者,现在是 Firefox 开发者工具团队的一员。
3 条评论