A vanilla virtual joystick for touch capable interfaces
Documentation · Interactive Demos · Migration Guide
options.zoneoptions.coloroptions.sizeoptions.thresholdoptions.fadeTimeoptions.multitouchoptions.maxNumberOfJoysticksoptions.dataOnlyoptions.positionoptions.modeoptions.restJoystickoptions.restOpacityoptions.catchDistanceoptions.lockXoptions.lockYoptions.shapeoptions.dynamicPageoptions.follownpm install nipplejs --save
Check out the interactive demos on the documentation website.
Import it the way you want into your project :
// CommonJS
var manager = require('nipplejs').create(options);
// AMD
define(['nipplejs'], function (nipplejs) {
var manager = nipplejs.create(options);
});
// Module
import nipplejs from 'nipplejs';
nipplejs.create(options);
<script src="https://github.com/yoannmoinet/nipplejs/raw/v1.0.4/nipplejs.js"></script>
<script>
var manager = nipplejs.create(options);
</script>
:warning: NB :warning: Your joystick's container must have a CSS position value set (relative, absolute, or fixed). Without it, the joystick will not be positioned correctly. The library warns in the console if position: static is detected.
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,
maxNumberOfJoysticks: 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|Object, // Re-center joystick on rest state
restOpacity: Number, // opacity when not 'dynamic' and rested
lockX: Boolean, // only move on the X axis
lockY: Boolean, // only move on the Y axis
catchDistance: Number, // distance to recycle previous joystick in 'semi' mode
shape: String, // 'circle' or 'square'
dynamicPage: Boolean, // Enable if the page has dynamically visible elements
follow: Boolean, // Makes the joystick follow the thumbstick
};
All options are optional :sunglasses:.
options.zoneDefaults to 'body'
The dom element in which all your joysticks will be injected.
<script type="text/javascript" src="https://github.com/yoannmoinet/nipplejs/raw/v1.0.4/nipplejs.js"></script>
<script type="text/javascript">
var options = {
zone: document.getElementById('zone_joystick'),
};
var manager = nipplejs.create(options);
</script>
This zone also serve as the mouse/touch events handler.
It represents the zone where all your joysticks will be active.
options.colorDefaults to 'white'
The background of your joystick's elements. Sets the CSS background property, so any valid value works — colors, gradients, images, etc.
Can be a single string (applied to both parts) or an object with front and back keys to style the thumb and base independently.
// Simple color
color: 'rebeccapurple',
// Gradient
color: 'linear-gradient(135deg, #818cf8, #6366f1)',
// Different front (thumb) and back (base)
color: {
front: 'linear-gradient(135deg, #818cf8, #38bdf8)',
back: 'rgba(99, 102, 241, 0.12)',
},
// Background image
color: {
front: 'url(thumb.png) center/cover',
back: 'radial-gradient(circle, rgba(99,102,241,0.15) 40%, transparent)',
},
options.sizeDefaults to 100
The size in pixel of the outer circle.
The inner circle is 50% of this size.
options.thresholdDefaults 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.fadeTimeDefaults to 250
The time it takes for joystick to fade-out and fade-in when activated or de-activated.
options.multitouchDefaults to false
Enable the multitouch capabilities.
If, for reasons, you need to have multiple nipples in 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.maxNumberOfJoysticksDefaults to 1
If you need to, you can also control the maximum number of instances that could be created.
Obviously in a multitouch configuration.
options.dataOnlyDefaults to false
The library won't draw anything in the DOM and will only trigger events with data.
options.positionDefaults 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.modeDefaults to 'dynamic'.
Three modes are possible :
'dynamic''semi'options.catchDistance of any previously created joystick.options.catchDistance a new direction is triggered immediately.options.catchDistance the previous joystick is destroyed and a new one is created.'static'options.position.options.restJoystickDefaults to true
Reset the joystick's position when it enters the rest state.
You can pass a boolean value to reset the joystick's position for both the axis.
var joystick = nipplejs.create({
restJoystick: true,
// This is converted to {x: true, y: true}
// OR
restJoystick: false,
// This is converted to {x: false, y: false}
});
Or you can pass an object to specify which axis should be reset.
var joystick = nipplejs.create({
restJoystick: {x: false},
// This is converted to {x: false, y: true}
// OR
restJoystick: {x: false, y: true},
});
options.restOpacityDefaults to 0.5
The opacity to apply when the joystick is in a rest position.
options.catchDistanceDefaults 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.lockXDefaults to false
Locks joystick's movement to the x (horizontal) axis
options.lockYDefaults to false
Locks joystick's movement to the y (vertical) axis
options.shapeDefaults to 'circle'
The shape of region within which joystick can move.
'circle'Creates circle region for joystick movement
'square'Creates square region for joystick movement
options.dynamicPageDefaults to false
Nuclear option: forces a recalculation of the joystick position on every single move event. This has a notable performance cost.
In most cases you don't need this — the zone is automatically watched with a ResizeObserver that handles size changes. For one-off layout changes (e.g. entering fullscreen, opening a sidebar), call manager.reposition() instead. Only enable dynamicPage as a last resort when the zone's position changes continuously without resizing (e.g. CSS animations that shift the zone).
options.followDefaults to false
Makes the joystick follow the thumbstick when it reaches the border. When the base moves, the move event includes baseDelta: { x, y } with the per-frame base displacement — use vector for fine aim and baseDelta for camera/world panning.
Your manager has the following signature :
{
on: Function, // handle internal event
off: Function, // un-handle internal event
destroy: Function, // destroy everything
reposition: Function, // recalculate joystick positions
uid: Number, // unique id of the manager
all: Map, // map of all joysticks by uid
options: {
zone: Element, // reactive zone
multitouch: Boolean,
maxNumberOfJoysticks: 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 wish to listen to internal events like :
manager.on('event#1 event#2', function (evt) {
// evt.type, evt.data
});
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.off('event', handler);
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.destroy()Gently remove all joysticks from the DOM and unbind all events.
manager.destroy();
manager.uidThe unique id of this manager.
manager.reposition()Recalculate the zone bounding box and all joystick positions. Call this after the zone element has been moved, resized, or when its layout has changed (e.g. entering fullscreen).
Note: the zone is automatically watched with a ResizeObserver, so you only need to call this manually for position changes that don't involve a resize.
manager.reposition();
Control the library's console output.
nipplejs.setLogLevel('debug'); // all logs
nipplejs.setLogLevel('info'); // info, warnings, errors
nipplejs.setLogLevel('warning'); // warnings and errors (default)
nipplejs.setLogLevel('error'); // errors only
nipplejs.setLogLevel('none'); // silent
nipplejs.getLogLevel(); // returns current level
Each joystick has the following signature :
{
on: Function,
off: Function,
destroy: Function,
setPosition: 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, // the joystick container element
front: Element, // the inner (movable) part
back: Element // the outer (static) part
},
options: {
color: String,
size: Number,
threshold: Number,
fadeTime: Number
}
}
joystick.on, joystick.offThe same as the manager.
joystick.uiThe object that stores the joystick's DOM elements.
{
el: Element, //
— the container
back: Element, //
— the outer circle
front: Element //
— the inner circle
}
$ claude mcp add nipplejs \
-- python -m otcore.mcp_server <graph>