navigator.getGamepads();
return an array of joysticks, Gamepad objects.window
object (namely, receiving joysticks from the navigator
, and events in the window
): "gamepadconnected", "gamepaddisconnected"
. window.addEventListener("gamepadconnected", function(e) {...})
e.gamepad
property is the joystick that is connected or disconnected.Gamepad
object itself:id
contains vendor id, product id (USB) and description. Record format is not regulated;index
which account is connected;mapping
line in which the spelling was written and if so, which one;connected
is the joystick connected
;timestamp DOMHighResTimeStamp
when the joystick data was last updated;axes
array of axes and values ​​from -1 to 1;buttons
array of buttons, objects containing pressed (boolean)
and value
[0; 1] Since triggers may have a smooth change in value, this should be taken into account sometimes.id
self-willed. In order to recognize the joystick yourself, you need to know the VID and PID, it means you need to parse this property, but the format is “dancing”: in the chromium, the line contains a description, and only then "Vendor: 092c Product: 12a8"
, in firefox, the line begins with them, separating by minuses , for example "092c-12a8-..."
, but the nastiest thing is that in the windows it turned out that prefilling with zeros is simply absent, therefore in Windows the line is transformed into "92c-12a8-..."
connected
;navigator.webkitGetGamepads()
to appear, the joystick must be active during a call to this function (for example, the button is pressed);maping
empty although there is remapping;buttons
array of values, not objects.Gamepad
objects are not updated until navigator.webkitGetGamepads()
or navigator.getGamepads()
is called (if it is, if it is, and the old version is called, then attention will be thrown and nothing will be updated). Those. getting an object from a function is not necessary to receive it again, but it is necessary to simply call this function.GamepadMap
class GamepadMap
rendered separately. An object created from this class can be passed to the interface constructor."id"
property. It is not safe on the one hand, but on the other parameter I could not find it better. Even the value of the mapping parameter does not give anything: in chromium, which only works with the webkit prefix, this parameter is empty, but the associations are ready, as I wrote above.gamepadconnected
and gamepaddisconnected
. Pressing the buttons and changes in the sticks should be obtained independently. Theoretically, this is useful, but in practice it is not always convenient. Especially if you create an alternative to "clavamyshi".navigator.getGamepads()
or navigator.webkitGetGamepads()
. In the firelight, everything is simpler, the state is updated automatically. Therefore, if the webkit, then pull this method every time before the survey.EventTarget
interface for the elements, but you can't just create and create extends EventTarget
. I had to “knee” my implementation, but observing the standard. Why not take ready Emet? There is no close compliance with the standard, but I wanted to do everything as standard where it is possible.EventTargetEmiter
class: class EventTargetEmiter # implements EventTarget ###* * . * @protected * @type Object ### _subscribe: null ###* * * @public * @type EventTargetEmiter ### parent: null ###* * . * @protected * @method _checkValues * @param String|* type * @param Handler|* listener - ### _checkValues: (type, listener) -> unless isString type ERR "type not string" return false unless isFunction listener ERR "listener is not a function" return false true ###* * `list` * handler- * @constructor * @param Array list ### constructor: (list...) -> @_subscribe = _length: 0 for e in list @_subscribe[e] = [] @['on' + e] = null ###* * Add function `listener` by `type` with `useCapture` * @public * @method addEventListener * @param String type * @param Handler listener * @param Boolean useCapture = false * @return void ### addEventListener: (type, listener, useCapture = false) -> unless @_checkValues(type, listener) return useCapture = not not useCapture @_subscribe[type].push [listener, useCapture] @_subscribe._length++ return ###* * Remove function `listener` by `type` with `useCapture` * @public * @method removeEventListener * @param String type * @param Handler listener * @param Boolean useCapture = false ### removeEventListener: (type, listener, useCapture = false) -> unless @_checkValues(type, listener) return useCapture = not not useCapture return unless @_subscribe[type]? for fn, i in @_subscribe[type] if fn[0] is listener and fn[1] is useCapture @_subscribe[type].splice i, 1 @_subscribe._length-- return return ###* * Burn, baby, burn! * @public * @method dispatchEvent * @param Event evt * @return Boolean ### dispatchEvent: (evt) -> unless evt instanceof Event ERR "evt is not event." return false t = evt.type unless @_subscribe[t]? throw new EventException "UNSPECIFIED_EVENT_TYPE_ERR" return false @emet t, evt ###* * Alias for addEventListener, but return this * @public * @method on * @param String type * @param Handler listener * @param Boolean useCapture * @return this ### on: (args...) -> @addEventListener args... @ ###* * Alias for removeEventListener * @public * @method off * @param String type * @param Handler listener * @param Boolean useCapture * @return this ### off: (args...) -> @removeEventListener args... @ ###* * Emiter event by `name` and create event or use `evt` if exist * @param String name * @param Event|null evt * @return Boolean ### emet: (name, evt = null) -> # run handled-style listeners r = @['on' + name](evt) if isFunction @['on' + name] return false if r is false # run other for fn in @_subscribe[name] try r = fn[0](evt) break if fn[1] is true or r is false if evt?.bubbles is true try @parent.emet name, evt if evt? then not evt.defaultPrevented else true
_subscribe
property _subscribe
accessible from the outside, but it doesn’t matter who rules the protective properties (with underlining) is ready to shoot himself in the foot. An object can be assigned a parent object, in which a “pop-up” event is transmitted.Event
, but simply create the Event
and set its properties to us is not allowed. CustomEvent
comes to the CustomEvent
, in which the detail
property is customizable. And so that the event is called and in the parent elements, do not forget to set canBubble
to true
in the constructor.requestAnimationFrame
used to poll the state. This is a plus and a minus:focus/blur
for the window, setInterval
for the scheduler, and a single requestAnimationFrame
for the first run (after all, the window can load in the background). Thus, the browser itself will be engaged in the list of tasks, will execute necessary between drawings. tick = (time, fn) -> # setInterval fn, time stopTick = (tickId) -> clearInterval tickId _startShedule: (Hz = 60) -> requestAnimationFrame = top.requestAnimationFrame or top.mozRequestAnimationFrame or top.webkitRequestAnimationFrame requestAnimationFrame => # t = null startTimers = -> t is null and t = tick (1000 / Hz |0), -> # , body() return stopTimers = -> if t isnt null # , stopTick(t) t = null return window.addEventListener 'focus', -> startTimers() window.addEventListener 'blur', -> stopTimers() startTimers() return return
navigator.getGamepads()
returns an array, so we need an array. But we would be eventful. This is where dances with a tambourine begin: to inherit an Array
you need to add a short line in the constructor: constructor: (items...) -> @splice 0, 0, items...
EventTargetEmiter
inherit. This could not be done directly in the cofescript. Therefore, I was helped by a simple function that passes the methods and properties to this
: _implements = (mixins...) -> for mixin in mixins @::[key] = value for key, value of mixin:: @
class EventedArray extends Array # implements EventTarget _implements.call(@, EventTargetEmiter) ###* * @constructor * @param items array-style constructor without single item as length. ### constructor: (items...) -> @splice 0, 0, items...
Gamepads
for working with joysticks, as well as Gamepad2
and GamepadMap
for manual and fine settings.Source: https://habr.com/ru/post/242835/
All Articles