Confirmed users
656
edits
No edit summary |
|||
| Line 1: | Line 1: | ||
== Web Joystick API (Draft Recommendation) == | |||
===== Abstract ===== | |||
The HTML5 specification introduces many of the necessary components for rich interactive and game development, from <canvas> and WebGL to <audio> and <video>. At the same time, JavaScript implementations have matured to the point where they can now support many tasks which previously required native code. The Web Joystick API presents a new way for web and game developers, as well as interaction designers, to access and use joysticks and other controllers for games. | |||
===== Authors ===== | |||
* Ted Mielczarek ([https://twitter.com/#!/TedMielczarek @TedMielczarek]) | |||
* Jason Orendorff ([https://twitter.com/#!/jorendorff @jorendorff]) | |||
* David Humphrey ([http://twitter.com/humphd @humphd]) | |||
* Kevin Gadd ([https://twitter.com/#!/antumbral @antumbral]) | |||
== API Tutorial == | |||
This API introduces new events on the window object for reading joystick and controller (hereby referred to as joystick) state. | |||
===== Connecting to a Joystick ===== | |||
When a new joystick is connected to the computer, the focused page will first receive a '''MozJoyConnected''' event. If a joystick was already connected when the page loaded, the '''MozJoyConnected''' event will be dispatched to the focused page when the user presses a button or moves an axis. Developers can use '''MozJoyConnected''' like this: | |||
<pre> | |||
<div id="joysticks"></div> | |||
<script> | |||
function joystickConnected(e) { | |||
var joysticks = document.getElementById("joysticks"), | |||
joystickId = e.joystick.id; | |||
joysticks.innerHTML += " Joystick Connected (id=" + joystickId + ")"; | |||
} | |||
window.addEventListener("MozJoyConnected", joystickConnected, false); | |||
</script> | |||
</pre> | |||
Each joystick will have a unique ID associated with it, which is available on the event's '''joystick''' property. | |||
===== Disconnecting a Joystick ===== | |||
When a joystick is disconnected, and if a page has previously received data (e.g., MozJoyConnected), a second event is dispatched to the focused window, '''MozJoyDisconnected''': | |||
<pre> | |||
<div id="joysticks"></div> | |||
<script> | |||
function joystickDisconnected(e) { | |||
var joysticks = document.getElementById("joysticks"), | |||
joystickId = e.joystick.id; | |||
joysticks.innerHTML += " Joystick Disconnected (id=" + joystickId + ")"; | |||
} | |||
window.addEventListener("MozJoyDisconnected", joystickDisconnected, false); | |||
</script> | |||
</pre> | |||
The Joystick's ID will be the same for '''MozJoyConnected''' and '''MozJoyDisconnected''' events, making it a suitable key for storing joystick device information: | |||
<pre> | |||
var joysticks = {}; | |||
function joystickHandler(event, connecting) { | |||
var joystick = event.joystick; | |||
if (connecting) { | |||
joysticks[joystick.id] = joystick; | |||
} else { | |||
delete joysticks[joystick.id]; | |||
} | |||
} | |||
window.addEventListener("MozJoyDisconnected", function(e) { joystickHandler(e, true); }, false); | |||
window.addEventListener("MozJoyDisconnected", function(e) { joystickHandler(e, false); }, false); | |||
</pre> | |||
This previous example also demonstrates how the '''joystick''' property can be held after the event has completed--a technique we will use for device state querying later. | |||
===== Joystick Button Events ===== | |||
Joysticks can have one or more buttons, and similar to a mouse button, this API provides events for buttons being pressed and released. When a joystick button is pressed a '''MozJoyButtonDown''' event is dispatched to the currently focused page. Similarly a '''MozJoyButtonUp''' event is dispatched when it is released. Both events provide the same '''joystick''' property, which indicates the joystick (i.e., it's ID) that triggered the event. The button itself (i.e., whichever of the 2, 4, etc. buttons the joystick has) is available on the event's '''button''' property. | |||
<pre> | |||
<div id="joysticks"></div> | |||
<script> | |||
function buttonHandler(event, pressed) { | |||
var joysticks = document.getElementById("joysticks"), | |||
joystickId = event.joystick.id, | |||
button = event.button, | |||
text = pressed ? " Joystick button pressed" : " Joystick button released"; | |||
joysticks.innerHTML += text + " (id=" + joystickId + ", button=" + button + )"; | |||
} | |||
window.addEventListener("MozJoyButtonDown", function(e) { buttonHandler(e, true); }, false); | |||
window.addEventListener("MozJoyButtonUp", function(e) { buttonHandler(e, false); }, false); | |||
</script> | |||
===== Joystick Axis Events ===== | |||
Similar to the button events, the axis events provide a way for developers to know when a user has moved one or more of a joystick's axises (i.e., a "stick"). Just as a joystick can have multiple buttons, so to can there be many axes. The '''MozJoyAxisMove''' event indicates that a joystick's axis has changed, and its new value. The axis is numbered, and its value is given, which is a float between -1.0 (the lowest possible value) and 1.0 (the highest possible value): | |||
<pre> | |||
<div id="joysticks"></div> | |||
<script> | |||
function axisHandler(event, pressed) { | |||
var joysticks = document.getElementById("joysticks"), | |||
joystickId = event.joystick.id, | |||
axis = event.axis, | |||
value = event.value; | |||
joysticks.innerHTML += " Joystick Axis Move (id=" + joystickId + ", axis=" + axis + ", value=" + value + ")"; | |||
} | |||
window.addEventListener("MozJoyAxisMove", axisHandler, false); | |||
</script> | |||
===== Querying the Joystick Object ===== | |||
All of the '''MozJoy*''' events discussed above included a '''joystick''' property on the event object. We used this in order to determine which joystick (i.e., it's ID) had caused the event, since multiple joysticks might be connected at once. | |||
We can do much more with this Joystick object, including holding a reference to it and querying it instead of using '''MozJoyButtonUp''', '''MozJoyButtonDown''', and '''MozJoyAxisMove''' events. Doing so is often desirable for games or other interactive web pages that need to know the state of a joystick now vs. the next time an event fires. | |||
As we have seen, the Joystick object for a given joystick is available on the '''MozJoyConnected''' event. For security reasons, it is not available as a property of the window object itself. Once we have a reference to it, we can query its properties for information about the current state of the joystick. Behind the scenes, this object will be updated every time the joystick's state changes. | |||
The Joystick object's properties include: | |||
* id : a unique id (string) for the joystick. | |||
* connected : true if the joystick is still connected to the system. | |||
* buttons : an array of the buttons present on the device. Each entry in the array is 0 if the button is not pressed, and non-zero if the button is pressed. | |||
* axes : an array of the axes present on the device. Each entry in the array is a float value in the range -1.0..1.0 representing the axis position from the lowest value (-1.0) to the highest value (1.0). | |||
The Joystick object is often used in conjunction with an animation loop (e.g., requestAnimationFrame), where developers want to make decisions for the current frame based on the state of the joystick or joysticks. | |||
Here is a complete example: | |||
<pre> | |||
<html> | |||
<head> | |||
<script type="text/javascript"> | |||
var joys = {}; | |||
function createDiv(joystick) { | |||
var d = document.createElement("div"); | |||
d.setAttribute("id", joystick.id); | |||
var t = document.createElement("h1"); | |||
t.appendChild(document.createTextNode("joystick: " + joystick.id)); | |||
d.appendChild(t); | |||
var b = document.createElement("div"); | |||
b.className = "buttons"; | |||
for (var i=0; i<joystick.buttons.length; i++) { | |||
var e = document.createElement("span"); | |||
e.className = "button"; | |||
e.innerHTML = i; | |||
b.appendChild(e); | |||
} | |||
d.appendChild(b); | |||
var a = document.createElement("div"); | |||
a.className = "axes"; | |||
for (var i=0; i<joystick.axes.length; i++) { | |||
var e = document.createElement("progress"); | |||
e.className = "axis"; | |||
e.setAttribute("max", "2"); | |||
e.setAttribute("value", "1"); | |||
e.innerHTML = i; | |||
a.appendChild(e); | |||
} | |||
d.appendChild(a); | |||
document.getElementById("start").style.display = "none"; | |||
return d; | |||
} | |||
function connectHandler(e) { | |||
var joy = e.joystick; | |||
joys[e.joystick.id] = joy; | |||
var div = createDiv(joy); | |||
document.body.appendChild(div); | |||
window.mozRequestAnimationFrame(updateStatus); | |||
} | |||
function disconnectHandler(e) { | |||
var d = document.getElementById(e.joystick.id); | |||
document.body.removeChild(d); | |||
delete joys[e.joystick.id]; | |||
} | |||
function updateStatus() { | |||
for (j in joys) { | |||
var joy = joys[j], | |||
d = document.getElementById(j), | |||
buttons = d.getElementsByClassName("button"), | |||
axes = d.getElementsByClassName("axis"); | |||
for (var i=0; i<joy.buttons.length; i++) { | |||
var b = buttons[i]; | |||
if (joy.buttons[i]) { | |||
b.className = "button pressed"; | |||
} | |||
else { | |||
b.className = "button"; | |||
} | |||
} | |||
for (var i=0; i<joy.axes.length; i++) { | |||
var a = axes[i]; | |||
a.innerHTML = i + ": " + joy.axes[i].toFixed(4); | |||
a.setAttribute("value", joy.axes[i] + 1); | |||
} | |||
} | |||
window.mozRequestAnimationFrame(updateStatus); | |||
} | |||
window.addEventListener("MozJoyConnected", connectHandler); | |||
window.addEventListener("MozJoyDisconnected", disconnectHandler); | |||
</script> | |||
<style> | |||
.buttons, .axes { | |||
padding: 1em; | |||
} | |||
.button { | |||
padding: 1em; | |||
border-radius: 20px; | |||
border: 1px solid black; | |||
} | |||
.pressed { | |||
background-color: green; | |||
} | |||
</style> | |||
</head> | |||
<body> | |||
<h2 id="start">Press a button on your controller to start</h2> | |||
</body> | |||
</html> | |||
</pre> | |||
.......still writing..... | |||
Joystick events are now sent only to the currently focused page. | |||
If you connect a new device with a page focused, and that page | |||
has previously received data from any device, a MozJoyConnected | |||
event will be sent immediately. | |||
{{bug|604039}} covers prototyping a way for web content to receive input from Joystick/gamepad controllers connected to a computer. | {{bug|604039}} covers prototyping a way for web content to receive input from Joystick/gamepad controllers connected to a computer. | ||