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
|
---
title: Pointer Lock API
slug: API/Pointer_Lock_API
translation_of: Web/API/Pointer_Lock_API
---
<p>{{ SeeCompatTable() }}</p>
<p><strong>指针锁定</strong>(以前叫做鼠标锁定) 提供了一种输入方法,这种方法是基于鼠标随着时间推移的运动的(也就是,deltas),而不仅是鼠标光标的绝对位置。通过它可以访问原始的鼠标运动,把鼠标事件的目标锁定到一个单独的元素,这就消除了鼠标在一个单独的方向上到底可以移动多远这方面的限制,并从视图中删去光标。</p>
<p>这个 API 对于需要大量的鼠标输入来控制运动,旋转物体,以及更改项目的应用程序来说非常有用。对高度视觉化的应用程序尤其重要,例如那些使用第一人称视角的应用程序,以及 3D 视图和建模。</p>
<p>举例来说,你可以创建让你的用户简单地通过移动鼠标而不需要点击任何按钮就可以控制视角的应用。那么这些按钮就可以被用作其他动作。这类鼠标输入对于查看地图,卫星图像,或者第一人称场景(例如在一个游戏中或者一个全景视频中)是非常方便使用的。</p>
<p>即使在光标移到浏览器或者屏幕区域之外,指针锁定也能够让你访问鼠标事件。例如,你的用户可以通过不断地移动鼠标来持续旋转或操纵一个 3D 模型。如果没有指针锁定的话,这些旋转或操纵会在指针到达浏览器或者屏幕边缘的那一刻停止。尤其是游戏玩家将会因为此功能而兴奋不已,因为他们可以疯狂地点击按钮,来回地滑动鼠标光标,而不必担心离开了游戏区域,进而不小心误点到另外一个应用程序上,结果将鼠标焦点移离了游戏。杯具了!</p>
<h2 id="basics" name="basics">基本概念</h2>
<p>指针锁定和<a href="/en/DOM/element.setCapture" title="element.setCapture">鼠标捕获</a>有关。鼠标捕获在一个鼠标被拖曳时可以向一个目标元素持续传递有关事件,但是当鼠标按钮被放开时就会停止。指针锁定和鼠标捕获在以下方面有所不同:</p>
<ul>
<li>它是持久性的。指针锁定不释放鼠标,直到作出一个显式的 API 调用或是用户使用一个专门的释放手势。</li>
<li>它不局限于浏览器或者屏幕边界。</li>
<li>它持续发送事件,而不管鼠标按钮状态如何。</li>
<li>它隐藏光标。</li>
</ul>
<h2 id="example" name="example">示例</h2>
<p>下面是一个如何在你的网页中设置指针锁定的示例。</p>
<pre class="brush: js notranslate"><button onclick="lockPointer();">锁住它!</button>
<div id="pointer-lock-element"></div>
<script>
// 注意: 截止本文撰写时, 仅有 Mozilla 和 WebKit 支持指针锁定。
// 我们将要使之全屏并指针锁定的元素。
var elem;
document.addEventListener("mousemove", function(e) {
var movementX = e.movementX ||
e.mozMovementX ||
e.webkitMovementX ||
0,
movementY = e.movementY ||
e.mozMovementY ||
e.webkitMovementY ||
0;
// 打印鼠标移动的增量值。
console.log("movementX=" + movementX, "movementY=" + movementY);
}, false);
function fullscreenChange() {
if (document.webkitFullscreenElement === elem ||
document.mozFullscreenElement === elem ||
document.mozFullScreenElement === elem) { // 较旧的 API 大写 'S'.
// 元素进入全屏模式了,现在我们可以请求指针锁定。
elem.requestPointerLock = elem.requestPointerLock ||
elem.mozRequestPointerLock ||
elem.webkitRequestPointerLock;
elem.requestPointerLock();
}
}
document.addEventListener('fullscreenchange', fullscreenChange, false);
document.addEventListener('mozfullscreenchange', fullscreenChange, false);
document.addEventListener('webkitfullscreenchange', fullscreenChange, false);
function pointerLockChange() {
if (document.mozPointerLockElement === elem ||
document.webkitPointerLockElement === elem) {
console.log("指针锁定成功了。");
} else {
console.log("指针锁定已丢失。");
}
}
document.addEventListener('pointerlockchange', pointerLockChange, false);
document.addEventListener('mozpointerlockchange', pointerLockChange, false);
document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
function pointerLockError() {
console.log("锁定指针时出错。");
}
document.addEventListener('pointerlockerror', pointerLockError, false);
document.addEventListener('mozpointerlockerror', pointerLockError, false);
document.addEventListener('webkitpointerlockerror', pointerLockError, false);
function lockPointer() {
elem = document.getElementById("pointer-lock-element");
// 开始于使元素进入全屏模式。目前的实现
// 要求元素在请求指针锁定前要处于全屏模式下
// -- 这在以后可能会发生改变。
elem.requestFullscreen = elem.requestFullscreen ||
elem.mozRequestFullscreen ||
elem.mozRequestFullScreen || // 较旧的 API 把 ‘S’ 大写
elem.webkitRequestFullscreen;
elem.requestFullscreen();
}
</script>
</pre>
<h2 id="method_overview" name="method_overview">方法/属性 概述</h2>
<p>Pointer lock API, 和 Fullscreen API 类似,通过添加新方法来扩展 DOM 元素, <code>requestPointerLock</code>, 目前还是厂商前缀。按下面这样来写:</p>
<pre class="brush: js notranslate"><span class="idlInterface" id="idl-def-MouseLockable"><span class="idlInterface" id="idl-def-MouseLockable">element.webkitRequestPointerLock(); // Chrome</span></span>
element.mozRequestPointerLock(); // Firefox
</pre>
<p>目前 <code>requestPointerLock</code> 的实现还是和 <code>requestFullScreen</code> 以及 Fullscreen API 紧紧地绑在一起的。一个元素在能够被指针锁定之前,必须首先进入全屏模式。就像上面演示的那样,锁定指针的过程是异步的,使用 (<code>pointerlockchange</code>, <code>pointerlockerror</code>) 事件来表明请求是成功还是失败了。这和 Fullscreen API 的工作方式是一致的,它使用 <code>requestFullScreen</code> 方法,以及 <code>fullscreenchange</code> 和 <code>fullscreenerror</code> 事件。</p>
<p>Pointer lock API 还扩展了 <code>document</code> 接口,添加了一个新的属性和一个新的方法。新的属性被用于访问当前被锁定的元素(如果有的话),并被命名为 <code>pointerLockElement</code>,目前也使用厂商前缀。 <code>document</code> 添加的新方法是 <code>exitPointerLock</code> ,顾名思义,它是用来退出指针锁定的。</p>
<p><code>pointerLockElement</code> 属性适用于确定当前是否有被指针锁定的元素(例如,用来做一个布尔检查),以及当有元素被锁定时获取该元素的一个引用。下面是这两种用法的一个例子:</p>
<pre class="brush: js notranslate"><span class="idlInterface" id="idl-def-MouseLockable"><span class="idlInterface" id="idl-def-MouseLockable">document.pointerLockElement = document.pointerLockElement ||
document.mozPointerLockElement ||
document.webkitPointerLockElement;</span></span>
// 1) 用于布尔检查--我们被指针锁定了吗?
if (!!document.pointerLockElement) {
// 指针被锁定
} else {
// 指针未被锁定
}
// 2) 用于访问指针锁定的元素
if (document.pointerLockElement === someElement) {
// someElement 当前被指针锁定
}
</pre>
<p><code>document</code> 的 <code>exitPointerLock</code> 方法被用来退出指针锁定,而且和 requestPointerLock 一样,使用 <code>pointerlockchange</code> 和 <code>pointerlockerror</code>事件以异步方式工作:</p>
<pre class="brush: js notranslate"><span class="idlInterface" id="idl-def-MouseLockable"><span class="idlInterface" id="idl-def-MouseLockable">document.exitPointerLock = document.exitPointerLock ||
document.mozExitPointerLock ||
document.webkitExitPointerLock;</span></span>
function pointerLockChange() {
<span class="idlInterface" id="idl-def-MouseLockable"><span class="idlInterface" id="idl-def-MouseLockable">document.pointerLockElement = document.pointerLockElement ||
document.mozPointerLockElement ||
document.webkitPointerLockElement;</span></span>
if (!!document.pointerLockElement) {
console.log("目前还是被锁定。");
} else {
console.log("已经退出锁定。");
}
}
document.addEventListener('pointerlockchange', pointerLockChange, false);
document.addEventListener('mozpointerlockchange', pointerLockChange, false);
document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
// 试图解除锁定
document.exitPointerLock();
</pre>
<h2 id="mouselocklostevent" name="mouselocklostevent">pointerlockchange 事件</h2>
<p>当指针锁定状态改变时 - 例如,当调用 <code>requestPointerLock</code>, <code>exitPointerLock</code>,用户按下 ESC 键,等等。— <code>pointerlockchange</code> 事件被分发到 <code>document</code>。 这是一个简单事件所以不包含任何的额外数据。</p>
<div class="note">该事件目前在 Firefox 中使用前缀的格式是 <code>mozpointerlockchange</code> ,在 Chrome 中是 <code>webkitpointerlockchange</code>。 </div>
<h2 id="mouselocklostevent" name="mouselocklostevent">pointerlockerror 事件</h2>
<p>当调用 <code>requestPointerLock</code> 或 <code>exitPointerLock</code>而引发错误时, <code>pointerlockerror</code> 事件被分发到 <code>document</code>。这是一个简单事件所以不包含任何的额外数据。</p>
<div class="note">该事件目前在 Firefox 中被加上前缀为 <code>mozpointerlockerror</code> ,在 Chrome 中为 <code>webkitpointerlockerror</code>。 </div>
<h2 id="extensions" name="extensions">鼠标事件扩展</h2>
<p>Pointer lock API 使用 movement 属性扩展了标准的 <code>MouseEvent</code>。</p>
<pre class="idl notranslate"><span class="idlInterface" id="idl-def-MouseEvent">partial interface <span class="idlInterfaceID">MouseEvent</span> {
<span class="idlAttribute"> readonly attribute <span class="idlAttrType"><a>long</a></span> <span class="idlAttrName"><a href="http://dvcs.w3.org/hg/webevents/raw-file/default/mouse-lock.html#widl-MouseEvent-movementX">movementX</a></span>;</span>
<span class="idlAttribute"> readonly attribute <span class="idlAttrType"><a>long</a></span> <span class="idlAttrName"><a href="http://dvcs.w3.org/hg/webevents/raw-file/default/mouse-lock.html#widl-MouseEvent-movementY">movementY</a></span>;</span>
};</span></pre>
<div class="note">movement 属性目前在 Firefox 中被加上前缀为 <code>.mozMovementX</code> 和 <code>.mozMovementY</code> , 在 Chrome 中为<code>.webkitMovementX</code> 和 <code>.webkitMovementY</code>。</div>
<p>鼠标事件的两个新参数—<code>movementX</code> 和 <code>movementY</code>—提供了鼠标位置的变化情况。这两个参数的值,等于两个<a href="/en/DOM/MouseEvent" title="en/DOM/Event/UIEvent/MouseEvent"><code>MouseEvent</code></a> 属性( <code>screenX</code> 和 <code>screenY)</code>之间值的变化程度,这些 MouseEvent 属性被存储在两个连续的鼠标移动事件( <code>eNow</code> 和 <code>ePrevious</code>)中。换言之,指针锁定参数 <code>movementX = eNow.screenX - ePrevious.screenX</code>。(译注:不存在名为 eNow 或 ePrevious 的事件或属性,eNow 代指当前的鼠标移动事件,ePrevious 代指前一个鼠标移动事件)</p>
<h3 id="locked_state" name="locked_state">锁定状态</h3>
<p>当指针锁定被启动之后,正常的 <code>MouseEvent</code> 属性 <code>clientX</code>, <code>clientY</code>, <code>screenX</code>, 和 <code>screenY</code> ,保持不变,就像鼠标没有在移动一样。 <code>movementX</code> 和 <code>movementY</code> 属性持续提供鼠标的位置变化。如果鼠标在一个方向上持续移动,<code>movementX</code> 和 <code>movementY</code>的值是没有限制的。不存在鼠标光标的概念,而且光标无法移到窗口之外,而且也不会被屏幕边缘所固定。</p>
<h3 id="unlocked_state" name="unlocked_state">未锁定状态</h3>
<p>无论鼠标锁定状态是怎样的, <code>movementX</code> 和 <code>movementY</code> 参数一直有效,并且为了方便起见,甚至在未锁定状态也是有效的。</p>
<p>当鼠标被解除锁定,系统光标可以退出并重新进入浏览器窗口。如果发生这种情况,<code>movementX</code> 和 <code>movementY</code> 可能会被设置成0。</p>
<h2 id="iframe_的限制">iframe 的限制</h2>
<p>指针锁定一次只能锁定一个 iframe。如果你锁定了一个 iframe,你不能试图锁定另外一个 iframe 然后把目标转移到这个 iframe 上;指针锁定将会出错。为了避免这一问题,首先解锁那个锁定的 iframe,然后再锁定另外一个。</p>
<p>在 iframe 默认的情况下, "sandboxed" iframes 会阻止指针锁定。避免这种限制的能力,即以属性/值 <code><iframe sandbox="allow-pointer-lock"></code> 组合的形式 , 有望很快在 Chrome 中出现。</p>
<h2 id="Browser_compatibiltiy" name="Browser_compatibiltiy">浏览器兼容性</h2>
<p>{{ CompatibilityTable() }}</p>
<div id="compat-desktop">
<table class="compat-table">
<tbody>
<tr>
<th>特性</th>
<th>Chrome</th>
<th>Firefox (Gecko)</th>
<th>Internet Explorer</th>
<th>Opera</th>
<th>Safari (WebKit)</th>
</tr>
<tr>
<td>基本支持</td>
<td>
<p>目标是 23{{ property_prefix("webkit") }}*</p>
<p>参见<a class="external" href="http://code.google.com/p/chromium/issues/detail?id=72754" title="http://code.google.com/p/chromium/issues/detail?id=72754"> CR/72574</a></p>
</td>
<td>
<p>{{ CompatGeckoDesktop("14.0") }}</p>
<p>{{bug("633602") }}</p>
</td>
<td>{{ CompatNo() }}</td>
<td>{{ CompatNo() }}</td>
<td>{{ CompatNo() }}</td>
</tr>
</tbody>
</table>
</div>
<div id="compat-mobile">
<table class="compat-table">
<tbody>
<tr>
<th>特性</th>
<th>Android</th>
<th>Firefox Mobile (Gecko)</th>
<th>IE Phone</th>
<th>Opera Mobile</th>
<th>Safari Mobile</th>
</tr>
<tr>
<td>基本支持</td>
<td>{{ CompatNo() }}</td>
<td>{{ CompatNo() }}</td>
<td>{{ CompatNo() }}</td>
<td>{{ CompatNo() }}</td>
<td>{{ CompatNo() }}</td>
</tr>
</tbody>
</table>
</div>
<p>* 在 <code>about:flags</code> 中要求这些特性被启用或是使用 <code>--enable-pointer-lock</code> 标记启动 Chrome。</p>
<h2 id="See_also" name="See_also">参见</h2>
<p>{{ spec("http://dvcs.w3.org/hg/pointerlock/raw-file/default/index.html", "W3C Pointer Lock API Specification", "ED") }}</p>
<p><a href="/en/DOM/MouseEvent" title="en/DOM/Event/UIEvent/MouseEvent">MouseEvent</a></p>
|