Library containing a custom joystick component and its TouchEventDemuxer, that allows for several joysticks to be moved simultaneously.
Contains:
-
Joystick
: A standalone retro style joystick component -
TouchEventDemuxer
: a high order component that intercepts touch events and dispatches them to its children, allowing for several components being moved simultaneously -
JoystickDemuxed
: A wrapper forJoystick
, making it compatible withTouchEventDemuxer
To use it, simply
npm install joystick-component-lib
Then import it in your project
import { Joystick } from 'joystick-component-lib';
// ...
React Native's touch event management is such that only one responder can be active at a time. While this is usually beneficial, effectively shutting down 'unwanted' touches when an action in ongoing, it can be a nuisance when trying to have several objects manipulable at the same time (such as being able to move a second joystick without releasing the first one). TouchEventDemuxer
is a solution to this problem, that intercepts all touch events happening in its children, analyzes them before dispatching them to the right owner.
import React from 'React';
import { TouchEventDemuxer, JoystickDemuxed } from 'joystick-component-lib';
const componentArray = [JoystickDemuxed, JoystickDemuxed];
DoubleJoystick = TouchEventDemuxer(componentArray);
// props are then passed in an array as follows:
class MyFancyComponent extends React.Component {
render() {
return (
<DoubleJoystick
childrenProps={[
{
// props for the first component of the componentArray
},
{
// props for the second component of the componentArray
},
]}
/>
);
}
}
To be compatible with TouchEventDemuxer
, children components must implement the method includes(x,y)
and can implement onTouchStart(touch)
onTouchMove(touch)
and onTouchEnd(touch)
, where:
-
includes(x, y)
is used by theTouchEventDemuxer
to ask its children if the touch hapening at the position(x, y)
"belongs" to them or not. It is advised to be careful when defining theincludes()
function, as two components should not return true for a same given position (no overlap). Note that if for some reason, several children return true for a given(x1, y1)
, then priority will be given to the first of these children in the children array. -
onTouch*(touch)
are callbacks triggered when theTouchEventDemuxer
gives ownership of a touch to the children component, and when subsequent moves happen until release.touch
has the following shape:-
responderID
: ID of the children controlling the touch, -
X0
: screen coordinates of the touch grant, -
Y0
: screen coordinates of the touch grant, -
pageX
: latest screen coordinates of the recently moved touch, -
pageY
: latest screen coordinates of the recently moved touch, -
dx
: accumulated distance of the gesture since the touch started -
dy
: accumulated distance of the gesture since the touch started
-
Simple, retro-style joystick than can be take either a vertical, circular or horizontal shape.
-
shape: Can be
circular
,horizontal
orvertical
. The value ofshape
will determine the effect of thelength
property -
length: Determine the characteristic length of the joystick, i.e. half its length for a
vertical
orhorizontal
joystick or its radius for acircular
one - neutralPointX: X Position of the center of the joystick
- neutralPointY: Y Position of the center of the joystick
-
onDraggableMove: will be called every time the handle is moved, with a
touch
object containing at leastdx
anddy
, displacement relative to the neutral point -
onDraggableRelease: will be called on handle release, with a similar
touch
object - onDraggableStart: will be called before the handle starts moving
note: the above props are different in case of a JoystickDemuxed
(see below).
- isSticky: If set to true, then the handle will automatically come back to the neutral point when released. Default to false
-
hasResponderOverride: Used as a standalone, the
Joystick
component comes with its ownPanResponder
and can therefore be used out of the box. When multiplesJoystick
are to be used simultaneously, thenhasResponderOverride
must be set totrue
in order to inhibate thePanRepsonder
and allow the parent component to take control. If you want to use this functionality however, it is recommended to directly use theTouchEventDemuxer
in conjunction with severalJoystickDemuxed
(see below for more details)
The default appearance is fully customizable through the props draggableStyle (will be applied to the handle) and draggableBackground. Note that the background's style will try to adapt to the one of the handle (by automatically extending it's width, for instance)
High order component that adds a functional layer to the Joystick
component in order to make it compatible with TouchEventDemuxer
. Properties are roughly identical to the regular Joystick
component, with a few differences:
-
onDraggableMove
,onDraggableStart
andonDraggableRelease
are replaced with the uniqueonJoystickMove
that is triggered on start, move and release with(xRatio, yRatio)
; wherexRatio = relative displacement / length value
(and similarly foryRatio
) and thus takes values between -1 and 1
If you wish to edit and modify this library, please feel free! Simply clone this repository, and run npm install
.
A "playground" (expo application to test out changes) is available, with a special launch script to make the expo launcher work with simlinks as long as the packages simlinked are include in the package.json
project dependencies.
To use it, go to the playground/
folder, then npm install
it. You can go back to the root folder and npm link
, then go to playground/
and npm link joystick-component-lib
. After having checked that joystick-component-lib
is indeed listed as a dependency in package.json
, launch the app using node react-native-start-with-link.js
.