aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/api/gamepad_api/using_the_gamepad_api/index.html
blob: dc220cc1ab41e23c0e5ed264e13a9831639f2e81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
---
title: 使用 Gamepad API
slug: Web/API/Gamepad_API/Using_the_Gamepad_API
tags:
  - 手柄
  - 指南
  - 控制器
  - 游戏
  - 进阶
translation_of: Web/API/Gamepad_API/Using_the_Gamepad_API
---
<p>{{DefaultAPISidebar("Gamepad API")}}{{ SeeCompatTable() }}</p>

<div class="summary">
<p><span class="seoSummary">HTML5 为丰富的交互式游戏开发引入了许多必要的组件。像 <code>&lt;canvas&gt;</code> 、WebGL、 <code>&lt;audio&gt;</code> 和 <code>&lt;video&gt;</code> 这样的技术,随着 JavaScript 的逐渐成熟,许多以前需要 native code 来实现的功能现在都可以实现了。Gamepad(手柄) API 是开发人员和设计者识别和使用游戏控制板和其他游戏控制器的一种方法。</span></p>
</div>

<p><a href="/en-US/docs/Web/API/Gamepad_API">Gamepad API</a> 引入新的事件在 {{ domxref("Window") }} 对象中,来读取手柄和控制器(以下称“控制器”)的状态。除此之外,API还添加了一个 {{ domxref("Gamepad") }} 对象,你可以用它来查询已连接控制器的状态;还有一个 {{ domxref("navigator.getGamepads()") }} 方法,你可以使用它来获取页面已知的控制器列表。</p>

<h2 id="conntecting" name="conntecting">连接控制器</h2>

<p>当一个新的手柄连接到计算机时,焦点页面(当前页面)首先接收一个 {{ event("gamepadconnected") }} 事件。 如果在加载页面时已经连接了手柄,则会在用户按下某个按钮或移动坐标方向(axes)时触发焦点页面的 {{ event("gamepadconnected") }} 事件。</p>

<div class="geckoVersionNote">
<p>在 Firefox 中,控制器只会暴露给与用户产生交互的可见页面。这有助于防止控制器被用于获取用户的指纹。一旦有一个手柄与页面产生交互,那么其他连接的控制器将自动对页面可见。</p>
</div>

<p>你可以这样使用 {{ event("gamepadconnected") }}</p>

<pre class="brush: js">window.addEventListener("gamepadconnected", function(e) {
  console.log("控制器已连接于 %d 位: %s. %d 个按钮, %d 个坐标方向。",
    e.gamepad.index, e.gamepad.id,
    e.gamepad.buttons.length, e.gamepad.axes.length);
});
</pre>

<p>每个控制器都有一个与之关联的唯一ID,其在事件的 {{domxref("GamepadEvent.gamepad", "gamepad")}} 属性上可用。</p>

<h2 id="disconnecting" name="disconnecting">断开控制器连接</h2>

<p>当控制器断开连接时, 如果页面以前接收过该手柄的数据 (例如 {{ event("gamepadconnected") }}),那么第二个事件 {{ event("gamepaddisconnected") }} 将会分配至焦点页面:</p>

<pre class="brush: js">window.addEventListener("gamepaddisconnected", function(e) {
  console.log("控制器已从 %d 位断开: %s",
    e.gamepad.index, e.gamepad.id);
});</pre>

<p>即使使用相同类型的多个控制器,控制器的 {{domxref("Gamepad.index", "index")}} 属性都会是唯一的,每一个设备都有一个。<code>index</code> 属性还可充当 {{ domxref("Navigator.getGamepads()") }} 返回 {{jsxref("Array")}} 的索引。</p>

<pre class="brush: js">var gamepads = {};

function gamepadHandler(event, connecting) {
  var gamepad = event.gamepad;
  // 注:
  // gamepad === navigator.getGamepads()[gamepad.index]

  if (connecting) {
    gamepads[gamepad.index] = gamepad;
  } else {
    delete gamepads[gamepad.index];
  }
}

window.addEventListener("gamepadconnected", function(e) { gamepadHandler(e, true); }, false);
window.addEventListener("gamepaddisconnected", function(e) { gamepadHandler(e, false); }, false);
</pre>

<p>上面的示例同时演示了在事件完成后如何保存 <code>gamepad</code> 属性,并在之后使用其查询设备状态。</p>

<h2 id="querying" name="querying">查询 Gamepad 对象</h2>

<p>正如你看到的,上面讨论的 <strong>gamepad</strong> 事件,包括事件对象上的 <code>gamepad</code> 属性,会返回一个 {{ domxref("Gamepad") }} 对象。因为可能同时连接不止一个控制器,所以我们可以使用它来确定是哪个控制器 (或者说 ID) 触发了事件。我们可以使用 {{ domxref("Gamepad") }} 对象做很多事,比如保留对象的引用并用其查询,以找出哪些按钮和摇杆在什么时候被按下了。相较于在下次触发,现在立即就可以获取控制器的状态对于游戏或其他交互式网页来说是一般是可取的。</p>

<p>开发者执行此类查询时往往涉及将 {{ domxref("Gamepad") }} 对象和一个动画循环 (例如 {{ domxref("Window.requestAnimationFrame","requestAnimationFrame") }})结合在一起,希望根据控制器的状态来对决定当前框架的行为。</p>

<div>
<p>{{ domxref("Navigator.getGamepads()") }} 方法返回当前对网页可见的所有设备的数组,{{ domxref("Gamepad") }} 对象 (初始值始终为 <code>null</code>,所以当没有控制器连接的时候将会返回 <code>null</code> )也一样可以用来获取的控制器信息。例如下面将会重写开头的第一个例子:</p>
</div>

<pre class="brush: js">window.addEventListener("gamepadconnected", function(e) {
  var gp = navigator.getGamepads()[e.gamepad.index];
  console.log("控制器已连接于 %d 位: %s. %d 个按钮, %d 个坐标方向。",
    gp.index, gp.id,
    gp.buttons.length, gp.axes.length);
});</pre>

<p>以下是 {{ domxref("Gamepad") }} 对象的属性说明:</p>

<ul>
 <li><code>id</code>: 包含一些关于控制器信息的字符串。它的格式没有特定规范,但是在 Firefox 中它将会含有三部分信息并以连接线连接 (<code>-</code>):包含控制器的 USB 供应商、产品 ID 的两个4位十六进制字符串以及驱动程序提供的控制器的名称。此信息旨在允许你为设备上的控件查找适当的映射, 并向用户显示有用的反馈。</li>
 <li><code>index</code>: 每个已连接手柄唯一的索引数字,可以用来区分多个控制器。请注意,断开目前的设备并连接一个新的设备可能会使用之前的索引值。</li>
 <li><code>mapping</code>: 一个用来指示浏览器是否已将设备上的控件重新映射到已知布局的字符串。目前只有一个受支持的已知布局 - <a href="https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#remapping">standard gamepad</a>(标准控制器)。如果浏览器能够将设备上的控件映射为该布局,<code>mapping</code> 属性将会为字符串 <code>standard</code></li>
 <li><code>connected</code>: 一个用来指示控制器是否连接到系统的布尔值。如果已连接则为 <code>True</code>;反之则为 <code>False</code></li>
 <li><code>buttons</code>: 一个表示设备上按钮的 {{ domxref("GamepadButton") }} 数组对象。每个 {{ domxref("GamepadButton") }} 都含有一个 <code>pressed</code> 和一个 <code>value</code> 属性:
  <ul>
   <li><code>pressed</code> 属性是一个表示按钮当前是按下 (<code>true</code>) 还是没按下 (<code>false</code>) 的布尔值。</li>
   <li><code>value</code> 属性是一个表示已触发模拟按钮 (例如目前很多控制器的线性/段式扳机键) 的值的浮点值。值的范围被规范为 0.0 到 1.0。0.0 表示没被按下的按钮,1.0 表示被完全按下的按钮。</li>
  </ul>
 </li>
 <li><code>axes</code>: 一个表示设备上坐标输入控件 (例如控制器摇杆) 的数组对象。数组中的每个值都是介于 -1.0 到 1.0 的浮点值,来表示坐标方向的最低 (-1.0) 和最大 (1.0) 值。</li>
 <li><code>timestamp</code>: 它将返回一个 {{ domxref("DOMHighResTimeStamp") }} ,该值表示上次更新此控制器数据的时间,以便开发者确定 <code>axes</code><code>button</code> 数据是否已从硬件更新。该值必须相对于 {{ domxref("PerformanceTiming") }} 接口的 <code>navigationStart</code> 对象。值是单调递增的,这意味着可以通过对比大小来确定数据更新的先后顺序,因为新的值始终比旧的值大。请注意Firefox当前不支持该属性。</li>
</ul>

<div class="note">
<p><strong>注:</strong>出于安全原因,Gamepad 对象在 {{ event("gamepadconnected") }} 事件上可用而在 {{ domxref("Window") }} 对象上不可用。一旦我们得到了对它的引用,我们就可以获取其属性以了解有关控制器当前状态的信息。在后台,此对象将会在控制器状态更改时更新。</p>
</div>

<h3 id="使用按键信息">使用按键信息</h3>

<p>让我们看一个简单的示例:显示一个控制器的连接信息 (忽略后续连接的控制器) ,并让您使用控制器右侧的四个操作按钮移动屏幕上一个球。你可以 <a href="http://chrisdavidmills.github.io/gamepad-buttons/">查看在线演示</a>,并可在 Github 上<a href="https://github.com/chrisdavidmills/gamepad-buttons/tree/master">找到源代码</a></p>

<p>我们首先声明一些变量:<code>gamepadInfo</code> 用于写入连接信息的段落;<code>ball</code> 是我们希望控制移动的球;<code>start</code> 作为 <code>requestAnimation Frame</code> ID 的初始变量; <code>a</code> 和 <code>b</code> 变量作为球位置动量,并且变量会被用于 {{ domxref("Window.requestAnimationFrame", "requestAnimationFrame()") }} 和 {{ domxref("Window.cancelAnimationFrame", "cancelAnimationFrame()") }} 。(?)</p>

<pre class="brush: js">var gamepadInfo = document.getElementById("gamepad-info");
var ball = document.getElementById("ball");
var start;
var a = 0;
var b = 0;
</pre>

<p>接下来我们使用 {{ event("gamepadconnected") }} 世界来检查控制器是否连接。当有一个控制连接时,我们就使用 {{ domxref("Navigator.getGamepads()") }}<code>[0]</code> 来抓取,输出控制器信息到我们“控制器信息”的 <code>div</code> 里,并开始 <code>gameLoop()</code> 函数来启动球的运动进程。</p>

<pre class="brush: js">window.addEventListener("gamepadconnected", function(e) {
  var gp = navigator.getGamepads()[e.gamepad.index];
  gamepadInfo.innerHTML = "控制器已连接于 " + gp.index + " 位:" + gp.id + "。它有 " + gp.buttons.length + " 个按钮和 " + gp.axes.length + " 个坐标方向。";

  gameLoop();
});</pre>

<p>现在我们再使用 {{ event("gamepaddisconnected") }} 事件来检查如果控制器断开的情况。如果断开了,我们会停止 {{ domxref("Window.requestAnimationFrame", "requestAnimationFrame()") }} 循环 (见下方) 并重置控制器信息到原来的样子。</p>

<pre class="brush: js">window.addEventListener("gamepaddisconnected", function(e) {
  gamepadInfo.innerHTML = "正在等待控制器。";

  cancelRequestAnimationFrame(start);
});</pre>

<p>Chrome 在这里有些区别。它没有在变量内不断的更新存储控制器的最后状态,而存储只是当时的一个快照,所以你要在 Chrome 中做到同样的事情的话,就需要不断地轮询,然后在可用的时候只能在代码中使用 {{ domxref("Gamepad") }} 对象来达到目的。我们下面用 {{ domxref("Window.setInterval()") }}实现了7;一旦控制器的可以输出了,游戏循环就会开始,可以使用 {{ domxref("Window.clearInterval()") }} 清除定时循环。请注意在较旧版本的 Chrome 中实现 {{ domxref("Navigator.getGamepads()") }} 需要加上  <code>webkit</code> 前缀。我们尝试对两种前缀版本都进行监测和处理,以向后兼容。</p>

<pre class="brush: js">var interval;

if (!('ongamepadconnected' in window)) {
  // 没有控制器事件可用,则开始轮询。
  interval = setInterval(pollGamepads, 500);
}

function pollGamepads() {
  var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
  for (var i = 0; i &lt; gamepads.length; i++) {
    var gp = gamepads[i];
    if (gp) {
      gamepadInfo.innerHTML = "控制器已连接于 " + gp.index + " 位:" + gp.id +
        "。它有 " + gp.buttons.length + " 个按钮和 " + gp.axes.length + " 个坐标方向。";
      gameLoop();
      clearInterval(interval);
    }
  }
}</pre>

<p>现在看主要的游戏循环。在每次我们所需的四个按钮被按下的时候进行处理。如果被按下了我就会适当地更新动量变量  <code>a</code> 和 <code>b</code> 的值,然后分别用 <code>a</code><code>b</code> 的值更新球的 {{ cssxref("left") }} 和 {{ cssxref("top") }} 属性。这样就可以在屏幕上移动数的位置了。在当前版本的 Chrome 中 (版本 34) button 的值是存储为数组的两个值,而不是 {{ domxref("GamepadButton") }} 对象。此问题已于开发者版本修复了。</p>

<p>当这些处理好之后,我们使用我们的 <font face="Consolas, Monaco, Andale Mono, monospace"><code>requestAnimationFrame()</code></font> 来请求下一个动画帧,然后运行 <code>gameLoop()</code> 再继续执行。</p>

<pre class="brush: js">function buttonPressed(b) {
<span class="k">  if</span> <span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span> <span class="o">==</span> <span class="s2">"object"</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">    return</span> <span class="nx">b</span><span class="p">.</span><span class="nx">pressed</span><span class="p">;</span>
<span class="nx">  </span><span class="p">}</span>
<span class="p">  return</span> <span class="nx">b</span> <span class="o">==</span> <span class="mf">1.0</span><span class="p">;</span>
}

function gameLoop() {
  var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []);
  if (!gamepads) {
    return;
  }

  var gp = gamepads[0];
  if (buttonPressed(gp.buttons[0])) {
    b--;
  } else if (buttonPressed(gp.buttons[2])) {
    b++;
  }
  if (buttonPressed(gp.buttons[1])) {
    a++;
  } else if (buttonPressed(gp.buttons[3])) {
    a--;
  }

  ball.style.left = a * 2 + "px";
  ball.style.top = b * 2 + "px";

  start = requestAnimationFrame(gameLoop);
}</pre>

<h3 id="使用坐标方向(axes)信息">使用坐标方向(axes)信息</h3>

<p><strong>待讨论 (除了一个用 axes[i] 一个用 button[i].value ,其他基本一样,Firefox 与 Chrome均是。)</strong></p>

<h2 id="完整的例子:显示控制器状态">完整的例子:显示控制器状态</h2>

<p>这个例子展示了怎样使用 {{ domxref("Gamepad") }} 对象,还有 {{ event("gamepadconnected") }} 和 {{ event("gamepaddisconnected") }} 事件显示所有已连接到系统的控制器的状态。你可以查看<a href="http://luser.github.io/gamepadtest/">在线演示</a>并且可在Github上看到<a href="https://github.com/luser/gamepadtest">完整的源代码</a></p>

<pre class="brush: js">var haveEvents = 'ongamepadconnected' in window;
var controllers = {};<span style="font-size: 1rem;">

</span><span style="font-size: 1rem;">function connecthandler(e) {
</span><span style="font-size: 1rem;">  addgamepad(e.gamepad);
</span><span style="font-size: 1rem;">}

</span><span style="font-size: 1rem;">function addgamepad(gamepad) {
</span><span style="font-size: 1rem;">  controllers[gamepad.index] = gamepad;

  var d = document.createElement("div");
</span><span style="font-size: 1rem;">  d.setAttribute("id", "controller" + gamepad.index);

</span><span style="font-size: 1rem;">  var t = document.createElement("h1");
</span><span style="font-size: 1rem;">  t.appendChild(document.createTextNode("控制器:" + gamepad.id));
</span><span style="font-size: 1rem;">  d.appendChild(t);

</span><span style="font-size: 1rem;">  var b = document.createElement("div");
</span><span style="font-size: 1rem;">  b.className = "buttons";
</span><span style="font-size: 1rem;">  for (var i = 0; i &lt; gamepad.buttons.length; i++) {
</span><span style="font-size: 1rem;">    var e = document.createElement("span");
</span><span style="font-size: 1rem;">    e.className = "button";
</span><span style="font-size: 1rem;">    //e.id = "b" + i;
</span><span style="font-size: 1rem;">    e.innerHTML = i;
</span><span style="font-size: 1rem;">    b.appendChild(e);
</span><span style="font-size: 1rem;">  }

</span><span style="font-size: 1rem;">  d.appendChild(b);

</span><span style="font-size: 1rem;">  var a = document.createElement("div");
</span><span style="font-size: 1rem;">  a.className = "axes";

</span><span style="font-size: 1rem;">  for (var i = 0; i &lt; gamepad.axes.length; i++) {
</span><span style="font-size: 1rem;">    var p = document.createElement("progress");
</span><span style="font-size: 1rem;">    p.className = "axis";
</span><span style="font-size: 1rem;">    //p.id = "a" + i;
</span><span style="font-size: 1rem;">    p.setAttribute("max", "2");
</span><span style="font-size: 1rem;">    p.setAttribute("value", "1");
</span><span style="font-size: 1rem;">    p.innerHTML = i;
</span><span style="font-size: 1rem;">    a.appendChild(p);
</span><span style="font-size: 1rem;">  }

</span><span style="font-size: 1rem;">  d.appendChild(a);

  // 见 </span>https://github.com/luser/gamepadtest/blob/master/index.html<span style="font-size: 1rem;">
  var start = </span><span style="font-size: 1rem;">document.getElementById("start");</span>
<span style="font-size: 1rem;">  if (start) {
</span><span style="font-size: 1rem;">    start.style.display = "none";
  }
</span><span style="font-size: 1rem;">
  document.body.appendChild(d);
</span><span style="font-size: 1rem;">  requestAnimationFrame(updateStatus);
</span><span style="font-size: 1rem;">}
</span><span style="font-size: 1rem;">
function disconnecthandler(e) {
</span><span style="font-size: 1rem;">  removegamepad(e.gamepad);
</span><span style="font-size: 1rem;">}

</span><span style="font-size: 1rem;">function removegamepad(gamepad) {
</span><span style="font-size: 1rem;">  var d = document.getElementById("controller" + gamepad.index);
</span><span style="font-size: 1rem;">  document.body.removeChild(d);
</span><span style="font-size: 1rem;">  delete controllers[gamepad.index];
</span><span style="font-size: 1rem;">}

</span><span style="font-size: 1rem;">function updateStatus() {
</span><span style="font-size: 1rem;">  if (!haveEvents) {
</span><span style="font-size: 1rem;">    scangamepads();
</span><span style="font-size: 1rem;">  }

  var i = 0;
  var j;

</span><span style="font-size: 1rem;">  for (j in controllers) {
</span><span style="font-size: 1rem;">    var controller = controllers[j];
</span><span style="font-size: 1rem;">    var d = document.getElementById("controller" + j);
</span><span style="font-size: 1rem;">    var buttons = d.getElementsByClassName("button");
</span><span style="font-size: 1rem;">
    for (i = 0; i &lt; controller.buttons.length; i++) {
</span><span style="font-size: 1rem;">      var b = buttons[i];
</span><span style="font-size: 1rem;">      var val = controller.buttons[i];
</span><span style="font-size: 1rem;">      var pressed = val == 1.0;
</span><span style="font-size: 1rem;">      if (typeof(val) == "object") {
</span><span style="font-size: 1rem;">        pressed = val.pressed;
</span><span style="font-size: 1rem;">        val = val.value;
</span><span style="font-size: 1rem;">      }

</span><span style="font-size: 1rem;">      var pct = Math.round(val * 100) + "%";
</span><span style="font-size: 1rem;">      b.style.backgroundSize = pct + " " + pct;

</span><span style="font-size: 1rem;">      if (pressed) {
</span><span style="font-size: 1rem;">        b.className = "button pressed";
</span><span style="font-size: 1rem;">      } else {
</span><span style="font-size: 1rem;">        b.className = "button";
</span><span style="font-size: 1rem;">      }
</span><span style="font-size: 1rem;">    }

</span><span style="font-size: 1rem;">    var axes = d.getElementsByClassName("axis");
</span><span style="font-size: 1rem;">    for (i = 0; i &lt; controller.axes.length; i++) {
</span><span style="font-size: 1rem;">      var a = axes[i];
</span><span style="font-size: 1rem;">      a.innerHTML = i + ": " + controller.axes[i].toFixed(4);
</span><span style="font-size: 1rem;">      a.setAttribute("value", controller.axes[i] + 1);
</span><span style="font-size: 1rem;">    }
</span><span style="font-size: 1rem;">  }

</span><span style="font-size: 1rem;">  requestAnimationFrame(updateStatus);
</span><span style="font-size: 1rem;">}

</span><span style="font-size: 1rem;">function scangamepads() {
</span><span style="font-size: 1rem;">  var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
</span><span style="font-size: 1rem;">  for (var i = 0; i &lt; gamepads.length; i++) {
</span><span style="font-size: 1rem;">    if (gamepads[i]) {
</span><span style="font-size: 1rem;">      if (gamepads[i].index in controllers) {
</span><span style="font-size: 1rem;">        </span><span style="font-size: 1rem;">controllers[gamepads[i].index] = gamepads[i];
</span><span style="font-size: 1rem;">      } else {
</span><span style="font-size: 1rem;">        </span><span style="font-size: 1rem;">addgamepad(gamepads[i]);
</span><span style="font-size: 1rem;">      }
</span><span style="font-size: 1rem;">    }
</span><span style="font-size: 1rem;">  }
</span><span style="font-size: 1rem;">}

</span><span style="font-size: 1rem;">
window.addEventListener("gamepadconnected", connecthandler);
</span><span style="font-size: 1rem;">window.addEventListener("gamepaddisconnected", disconnecthandler);

</span><span style="font-size: 1rem;">if (!haveEvents) {
</span><span style="font-size: 1rem;">  setInterval(scangamepads, 500);
</span><span style="font-size: 1rem;">}</span></pre>

<h2 id="规范">规范</h2>

<table class="standard-table">
 <tbody>
  <tr>
   <th scope="col">规范</th>
   <th scope="col">状态</th>
   <th scope="col">备注</th>
  </tr>
  <tr>
   <td>{{SpecName("Gamepad", "#gamepad-interface", "Gamepad")}}</td>
   <td>{{Spec2("Gamepad")}}</td>
   <td>Initial defintion</td>
  </tr>
 </tbody>
</table>

<h2 id="浏览器兼容性">浏览器兼容性</h2>

<p>{{Compat("api.Gamepad")}}</p>

<p> </p>