Thumbstick
A virtual joystick for touch capable interfaces
This project owes a great deal of debt to the rather unfortunately-named NippleJS. For now this project is basically a straight fork, which I intend to keep up-to-date with upstream. That may eventually change.
Table Of Contents
- Install
- Demo
- Usage
- Options
options.zone
defaults to 'body'options.color
defaults to 'white'options.size
defaults to 100options.threshold
defaults to 0.1options.fadeTime
defaults to 250options.multitouch
defaults to falseoptions.maxNumberOfThumbs
defaults to 1options.dataOnly
defaults to falseoptions.position
defaults to{top: 0, left: 0}
options.mode
defaults to 'dynamic'.options.restJoystick
defaults to trueoptions.restOpacity
defaults to 0.5options.catchDistance
defaults to 200
- API
- Events
- Contributing
Install
npm install thumbstick --save // OR bower install thumbstick --save
Demo
Check out the demo here.
Usage
Import it the way you want into your project :
// CommonJSvar manager = ;
// AMD;
// Global
⚠️ NB ⚠️ Your joystick's container has to have its CSS position
property set, either absolute
, relative
, static
, ....
Options
You can configure your joystick in different ways :
var options = zone: Element // active zone color: String size: Integer threshold: Float // before triggering a directional event fadeTime: Integer // transition time multitouch: Boolean maxNumberOfThumbs: Number // when multitouch, what is too many? dataOnly: Boolean // no dom element whatsoever position: Object // preset position for 'static' mode mode: String // 'dynamic', 'static' or 'semi' restJoystick: Boolean restOpacity: Number // opacity when not 'dynamic' and rested catchDistance: Number // distance to recycle previous joystick in // 'semi' mode;
All options are optional 😎.
options.zone
defaults to 'body'
The dom element in which all your joysticks will be injected.
This zone also serve as the mouse/touch events handler.
It represents the zone where all your joysticks will be active.
options.color
defaults to 'white'
The background color of your joystick's elements.
Can be any valid CSS color.
options.size
defaults to 100
The size in pixel of the outer circle.
The inner circle is 50% of this size.
options.threshold
defaults to 0.1
This is the strength needed to trigger a directional event.
Basically, the center is 0 and the outer is 1.
You need to at least go to 0.1 to trigger a directional event.
options.fadeTime
defaults to 250
The time it takes for joystick to fade-out and fade-in when activated or de-activated.
options.multitouch
defaults to false
Enable the multitouch capabilities.
If, for reasons, you need to have multiple thumbs into the same zone.
Otherwise it will only get one, and all new touches won't do a thing.
Please note that multitouch is off when in static
or semi
modes.
options.maxNumberOfThumbs
defaults to 1
If you need to, you can also control the maximum number of instance that could be created.
Obviously in a multitouch configuration.
options.dataOnly
defaults to false
The library won't draw anything in the DOM and will only trigger events with data.
options.position
defaults to {top: 0, left: 0}
An object that will determine the position of a static
mode.
You can pass any of the four top
, right
, bottom
and left
.
They will be applied as any css property.
Ex :
{top: '50px', left: '50px'}
{left: '10%', bottom: '10%'}
options.mode
defaults to 'dynamic'.
Three modes are possible :
'dynamic'
- a new joystick is created at each new touch.
- the joystick gets destroyed when released.
- can be multitouch.
'semi'
- new joystick is created at each new touch farther than
options.catchDistance
of any previously created joystick. - the joystick is faded-out when released but not destroyed.
- when touch is made inside the
options.catchDistance
a new direction is triggered immediately. - when touch is made oustide the
options.catchDistance
the previous joystick is destroyed and a new one is created. - cannot be multitouch.
'static'
- a joystick is positionned immediately at
options.position
. - one joystick per zone.
- each new touch triggers a new direction.
- cannot be multitouch.
options.restJoystick
defaults to true
Reset the joystick's position when it enters the rest state.
options.restOpacity
defaults to 0.5
The opacity to apply when the joystick is in a rest position.
options.catchDistance
defaults to 200
This is only useful in the semi
mode, and determine at which distance we recycle the previous joystick.
At 200 (px), if you press the zone into a rayon of 200px around the previously displayed joystick,
it will act as a static
one.
options.lockX
defaults to false
Locks joystick's movement to the x (horizontal) axis
options.lockY
defaults to false
Locks joystick's movement to the y (vertical) axis
API
Thumbstick instance (manager)
Your manager has the following signature :
on: Function // handle internal event off: Function // un-handle internal event get: Function // get a specific joystick destroy: Function // destroy everything ids: Array // array of assigned ids id: Number // id of the manager options: zone: Element // reactive zone multitouch: Boolean maxNumberOfThumbs: Number mode: String position: Object catchDistance: Number size: Number threshold: Number color: String fadeTime: Number dataOnly: Boolean restJoystick: Boolean restOpacity: Number
manager.on(type, handler)
If you whish to listen to internal events like :
manager;
Note that you can listen to multiple events at once by separating them either with a space or a comma (or both, I don't care).
manager.off([type, handler])
To remove an event handler :
manager;
If you call off without arguments, all handlers will be removed.
If you don't specify the handler but just a type, all handlers for that type will be removed.
manager.get(identifier)
An helper to get an instance via its identifier.
// Will return the thumbstick instanciated by the touch identified by 0manager;
manager.destroy()
Gently remove all thumbs from the DOM and unbind all events.
manager;
manager.ids
The array of thumbs' ids under this manager.
manager.id
The incremented id of this manager.
thumbstick instance (joystick)
Each joystick has the following signature :
on: Function off: Function el: Element show: Function // fade-in hide: Function // fade-out add: Function // inject into dom remove: Function // remove from dom destroy: Function identifier: Number trigger: Function position: // position of the center x: Number y: Number frontPosition: // position of the front part x: Number y: Number ui: el: Element front: Element back: Element options: color: String size: Number threshold: Number fadeTime: Number
joystick.on
, joystick.off
The same as the manager.
joystick.el
Dom element in which the joystick gets created.
joystick.show([cb])
Will show the joystick at the last known place.
You can pass a callback that will be executed at the end of the fade-in animation.
joystick.hide([cb])
Will fade-out the joystick.
You can pass a callback that will be executed at the end of the fade-out animation.
joystick.add()
Add the joystick's element to the dom.
joystick.remove()
Remove the joystick's element from the dom.
joystick.destroy()
Gently remove this thumbstick from the DOM and unbind all related events.
joystick.identifier
Returns the unique identifier of the joystick.
Tied to its touch's identifier.
joystick.trigger(type [, data])
Trigger an internal event from the joystick.
The same as on
you can trigger multiple events at the same time.
joystick.position
The absolute position of the center of the joystick.
joystick.frontPosition
The absolute position of the back part of the joystick's ui.
joystick.ui
The object that store its ui elements
{ el: back: front: }
Events
You can listen events both on the manager and all the joysticks.
But some of them are specific to its instance.
If you need to listen to each joystick, for example, you can :
manager ;
manager only
added
A joystick just got added.
Will pass the instance alongside the event.
removed
A joystick just got removed.
Fired at the end of the fade-out animation.
Will pass the instance alongside the event.
Won't be trigger in a dataOnly
configuration.
manager and joysticks
Other events are available on both the manager and joysticks.
When listening on the manager,
you can also target a joystick in particular by prefixing
the event with its identifier, 0:start
for example.
Else you'll get all events from all the joysticks.
start
A joystick is activated. (the user pressed on the active zone)
Will pass the instance alongside the event.
end
A joystick is de-activated. (the user released the active zone)
Will pass the instance alongside the event.
move
A joystick is moved.
Comes with data :
identifier: 0 // the identifier of the touch/mouse that triggered it position: // absolute position of the center in pixels x: 125 y: 95 force: 02 // strength in % distance: 254 // distance from center in pixels pressure: 01 // the pressure applied by the touch angle: radian: 15707963268 // angle in radian degree: 90 instance: Thumb // the thumbstick instance that triggered the event
dir
When a direction is reached after the threshold.
Direction are split with a 45° angle.
// \ UP /// \ /// LEFT RIGHT// / \// /DOWN \
You can also listen to specific direction like :
dir:up
dir:down
dir:right
dir:left
In this configuration only one direction is triggered at a time.
plain
When a plain direction is reached after the threshold.
Plain directions are split with a 90° angle.
// UP |// ------ LEFT | RIGHT// DOWN |
You can also listen to specific plain direction like :
plain:up
plain:down
plain:right
plain:left
In this configuration two directions can be triggered at a time,
because the user could be both up
and left
for example.
shown
Is triggered at the end of the fade-in animation.
Will pass the instance alongside the event.
Won't be trigger in a dataOnly
configuration.
hidden
Is triggered at the end of the fade-out animation.
Will pass the instance alongside the event.
Won't be trigger in a dataOnly
configuration.
destroyed
Is triggered at the end of destroy.
Will pass the instance alongside the event.
pressure
MBP's Force Touch, iOS's 3D Touch, Microsoft's pressure or MDN's force
Is triggered when the pressure on the joystick is changed.
The value, between 0 and 1, is sent back alongside the event.
Contributing
Your help is more than welcome, I would be very honored to have you on my side.
Here are some very basic guidelines.
Commits
Please follow these guidelines so your commits will be taken by the self-generated changelog.
Style
There are both JSCS and ESLint in the project.
To test your code against them simply run npm run prebuild
.
We follow a 4 spaces rule around here.
Workflow
You can use the available scripts if needed.
npm run watch
will run the build each time a change is detected.npm run formatAndLint
will test the formatting and the linting of your code.npm start
will run a static server that will serve thetest/
folder onlocalhost:8080
.npm test
will test using CasperJS, you have to runnpm start
in another window to have a local server available to CasperJS.
Build
Once you're satisfied with your changes, you can also include a build.
npm run build
to generate built files.- commit your build with the message
chore: new build
. npm version patch|minor|major
depending on your change. Changelog will be generated and bower's version synced and everything is automatically committed (not pushed though).