Skip to content

Commit

Permalink
feat(follow): Add option to follow thumbstick (#160)
Browse files Browse the repository at this point in the history
Co-authored-by: Yoann Moinet <yo@nnmoi.net>
  • Loading branch information
emepetres and yoannmoinet committed Feb 24, 2021
1 parent 5717293 commit 5c4c002
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 28 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
+ [`'circle'`](#circle)
+ [`'square'`](#square)
* [`options.dynamicPage` defaults to false](#optionsdynamicpage-defaults-to-false)
* [`options.follow` defaults to false](#optionsfollow-defaults-to-false)
- [API](#api)
* [NippleJS instance (manager)](#nipplejs-instance-manager)
+ [`manager.on(type, handler)`](#managerontype-handler)
Expand Down Expand Up @@ -147,6 +148,7 @@ var options = {
// '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
};
```

Expand Down Expand Up @@ -270,6 +272,9 @@ Creates square region for joystick movement
### `options.dynamicPage` defaults to false
Enable if the page has dynamically visible elements such as for Vue, React, Angular or simply some CSS hiding or showing some DOM.

### `options.follow` defaults to false
Makes the joystick follow the thumbstick when it reaches the border.

----

## API
Expand Down
45 changes: 33 additions & 12 deletions src/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ function Collection (manager, options) {
lockX: false,
lockY: false,
shape: 'circle',
dynamicPage: false
dynamicPage: false,
follow: false
};

self.config(options);
Expand Down Expand Up @@ -117,7 +118,7 @@ Collection.prototype.begin = function () {
// Nipple Factory
Collection.prototype.createNipple = function (position, identifier) {
var self = this;
var scroll = u.getScroll();
var scroll = self.manager.scroll;
var toPutOn = {};
var opts = self.options;

Expand Down Expand Up @@ -363,6 +364,7 @@ Collection.prototype.processOnMove = function (evt) {
var opts = self.options;
var identifier = self.manager.getIdentifier(evt);
var nipple = self.nipples.get(identifier);
var scroll = self.manager.scroll;

// If we're moving without pressing
// it's that we went out the active zone
Expand All @@ -381,11 +383,10 @@ Collection.prototype.processOnMove = function (evt) {
}

if (opts.dynamicPage) {
var scroll = u.getScroll();
pos = nipple.el.getBoundingClientRect();
var elBox = nipple.el.getBoundingClientRect();
nipple.position = {
x: scroll.x + pos.left,
y: scroll.y + pos.top
x: scroll.x + elBox.left,
y: scroll.y + elBox.top
};
}

Expand Down Expand Up @@ -415,14 +416,34 @@ Collection.prototype.processOnMove = function (evt) {
};

// Clamp the position
if(nipple.options.shape === 'circle'){
var clamped_dist;
var clamped_pos;
if (nipple.options.shape === 'circle') {
// Clamp to a circle
dist = Math.min(dist,size);
pos = u.findCoord(nipple.position, dist, angle);
}else{
clamped_dist = Math.min(dist, size);
clamped_pos = u.findCoord(nipple.position, clamped_dist, angle);
} else {
// Clamp to a square
pos = u.clamp(pos, nipple.position, size);
dist = u.distance(pos, nipple.position);
clamped_pos = u.clamp(pos, nipple.position, size);
clamped_dist = u.distance(clamped_pos, nipple.position);
}

if (opts.follow) {
// follow behaviour
if (dist > size) {
let delta_x = pos.x - clamped_pos.x;
let delta_y = pos.y - clamped_pos.y;
nipple.position.x += delta_x;
nipple.position.y += delta_y;
nipple.el.style.top = (nipple.position.y - (self.box.top + scroll.y)) + 'px';
nipple.el.style.left = (nipple.position.x - (self.box.left + scroll.x)) + 'px';

dist = u.distance(pos, nipple.position);
}
} else {
// clamp behaviour
pos = clamped_pos;
dist = clamped_dist;
}

var xPosition = pos.x - nipple.position.x;
Expand Down
38 changes: 23 additions & 15 deletions src/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,35 @@ function Manager (options) {
self.ids = {};
self.index = 0;
self.collections = [];
self.scroll = u.getScroll();

self.config(options);
self.prepareCollections();

// Listen for resize, to reposition every joysticks
var resizeTimer;
u.bindEvt(window, 'resize', function (evt) {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
var pos;
var scroll = u.getScroll();
self.collections.forEach(function (collection) {
collection.forEach(function (nipple) {
pos = nipple.el.getBoundingClientRect();
nipple.position = {
x: scroll.x + pos.left,
y: scroll.y + pos.top
};
});
var resizeHandler = function () {
var pos;
self.collections.forEach(function (collection) {
collection.forEach(function (nipple) {
pos = nipple.el.getBoundingClientRect();
nipple.position = {
x: self.scroll.x + pos.left,
y: self.scroll.y + pos.top
};
});
}, 100);
});
};
u.bindEvt(window, 'resize', function () {
u.throttle(resizeHandler);
});

// Listen for scrolls, so we have a global scroll value
// without having to request it all the time.
var scrollHandler = function () {
self.scroll = u.getScroll();
};
u.bindEvt(window, 'scroll', function () {
u.throttle(scrollHandler);
});

return self.collections;
Expand Down
8 changes: 8 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ export const isPressed = (evt) => {
return evt.buttons !== 0;
};

const timers = new Map();
export const throttle = (cb) => {
if (timers.has(cb)) {
clearTimeout(timers.get(cb));
}
timers.set(cb, setTimeout(cb, 100));
};

export const bindEvt = (el, arg, handler) => {
const types = arg.split(/[ ,]+/g);
let type;
Expand Down
9 changes: 8 additions & 1 deletion types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,17 @@ export interface JoystickManagerOptions {

/**
* Defaults to `circle`
*
*
* Sets the shape of the joystick
*/
shape?: 'circle' | 'square';

/**
* Defaults to `false`
*
* Make the joystick follow the cursor beyond its limits.
*/
follow?: boolean;
}

export interface Position {
Expand Down

0 comments on commit 5c4c002

Please sign in to comment.