Skip to content

Commit

Permalink
new rollover event, throttling for drag and rollover, improve perform…
Browse files Browse the repository at this point in the history
…ance
  • Loading branch information
robinrodricks committed May 3, 2021
1 parent ca6ece6 commit 0dd29b3
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 28 deletions.
39 changes: 32 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Features:
- Automatically add styling on hover and tap using `v-touch-class` directive
- Bind multiple touch events on one DOM element
- Customizable events with native-like events handler
- Throttling for `drag` and `rollover` events to prevent crashing your application
- Global configuration that applies to all events in the application
- Ability to override global configuration on any element using `v-touch-options` directive
- Bindings for TypeScript included and also works in pure-JavaScript projects
Expand Down Expand Up @@ -167,8 +168,9 @@ List of all supported events are given below.
| `v-touch:hold` | Triggered when the user holds the mouse button down for `touchHoldTolerance` MS while over the element (press and hold). <br> This will be triggered before your finger is released, similar to what native mobile apps do. |
| `v-touch:press` | **Desktop:** Triggered when the user presses the element (mouse down). <br> **Mobile:** Triggered when the user taps the element without releasing. |
| `v-touch:drag.once` | Triggered when the user presses and drags the element. <br> Only fired once, the moment the user first drags on the element. |
| `v-touch:drag` | Triggered when the user presses and drags the element. <br> Fired every time the mouse moves while dragging the element. |
| `v-touch:drag` | Triggered when the user presses and drags the element. <br> Fired every time the mouse moves while dragging the element. <br> This event is throttled to prevent too many events per second. <br> This event will fire every `dragFrequency` MS. |
| `v-touch:release` | **Desktop:** Triggered when the user releases the element (mouse up). <br> **Mobile:** Triggered when the user taps and releases the element. |
| `v-touch:rollover` | **Desktop only:** Triggered when the user moves his mouse over the element. <br> This event is throttled to prevent too many events per second. <br> This event will fire every `rollOverFrequency` MS. |



Expand All @@ -186,6 +188,30 @@ Some events have been renamed from the vue 2.x version of this library, in order



### System events

These are the default interactivity events supported by vue.js 3.x.

* You do not need to install this library to use them.
* They are always available for your usage alongside this library.
* The system default `mousemove` event is similar to `v-touch:rollover`, however the system event is not throttled and it will trigger hundreds of times per second, potentially crashing your application if any logic is performed in the event handler

**[Desktop devices](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent):**
- `v-on:click` - Triggered when the user presses and releases the element.
- `v-on:mousedown` - Triggered when the user presses the element.
- `v-on:mousemove` - Triggered when the user moves the mouse over the element.
- `v-on:mouseup` - Triggered when the user presses and releases the element.
- `v-on:mouseenter` - Triggered when the user moves his mouse into the element.
- `v-on:mouseleave` - Triggered when the user moves his mouse away from the element.

**[Mobile devices](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events):**
- `v-on:touchstart` - Triggered when the user presses the element.
- `v-on:touchmove` - Triggered when the user presses and drags over the element.
- `v-on:touchcancel` - Triggered when the user presses the element, and releases outside the element, thereby cancelling his tap.
- `v-on:touchend` - Triggered when the user taps the element (press and release).



## Options

These additional directives can be added to each element.
Expand Down Expand Up @@ -262,12 +288,7 @@ span.active {

```js
Vue.use(Vue3TouchEvents, {
disableClick: false,
touchClass: "",
tapTolerance: 10,
touchHoldTolerance: 400,
swipeTolerance: 30,
longTapTimeInterval: 400,
disableClick: false, ...
});
```

Expand All @@ -289,3 +310,7 @@ Vue.use(Vue3TouchEvents, {

- `longTapTimeInterval` in milliseconds - The minimum time interval to detect whether long tap event effective or not. **Default:** `400` MS.

- `dragFrequency` in milliseconds - How often should `drag` events be fired. **Default:** `100` MS (10 times a second).

- `rollOverFrequency` in milliseconds - How often should `rollover` events be fired. **Default:** `100` MS (10 times a second).

88 changes: 67 additions & 21 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ var isPassiveSupported = (function() {

var vueTouchEvents = {
install: function (app, constructorOptions) {

var globalOptions = Object.assign({}, {
disableClick: false,
tapTolerance: 10, // px
swipeTolerance: 30, // px
touchHoldTolerance: 400, // ms
longTapTimeInterval: 400, // ms
touchClass: ''
touchClass: '',
dragFrequency: 100, // ms
rollOverFrequency: 100, // ms
}, constructorOptions);

function touchStartEvent(event) {
Expand Down Expand Up @@ -75,13 +78,22 @@ var vueTouchEvents = {
$this.currentY = 0;

$this.touchStartTime = event.timeStamp;

// Trigger touchhold event after `touchHoldTolerance`ms
$this.touchHoldTimer = setTimeout(function() {
$this.touchHoldTimer = null;
triggerEvent(event, $el, 'hold');
}, $this.options.touchHoldTolerance);


// performance: only process swipe events if `swipe.*` event is registered on this element
$this.hasSwipe = hasEvent(this, 'swipe')
|| hasEvent(this, 'swipe.left') || hasEvent(this, 'swipe.right')
|| hasEvent(this, 'swipe.top') || hasEvent(this, 'swipe.bottom');

// performance: only start hold timer if the `hold` event is registered on this element
if (hasEvent(this, 'hold')){

// Trigger touchhold event after `touchHoldTolerance` MS
$this.touchHoldTimer = setTimeout(function() {
$this.touchHoldTimer = null;
triggerEvent(event, $el, 'hold');
}, $this.options.touchHoldTolerance);
}

triggerEvent(event, this, 'press');
}

Expand All @@ -102,20 +114,45 @@ var vueTouchEvents = {
$this.touchMoved = Math.abs($this.startX - $this.currentX) > tapTolerance ||
Math.abs($this.startY - $this.currentY) > tapTolerance;

// trigger `drag.once` only once after mouse FIRST moved while dragging the element
// (`touchMoved` is the flag that indicates we no longer need to trigger this)
if($this.touchMoved){
cancelTouchHoldTimer($this);
triggerEvent(event, this, 'drag.once');
}

} else if (!$this.swipeOutBounded) {
var swipeOutBounded = $this.options.swipeTolerance;
// performance: only process swipe events if `swipe.*` event is registered on this element
} else if ($this.hasSwipe && !$this.swipeOutBounded) {
var swipeOutBounded = $this.options.swipeTolerance;

$this.swipeOutBounded = Math.abs($this.startX - $this.currentX) > swipeOutBounded &&
Math.abs($this.startY - $this.currentY) > swipeOutBounded;
}

$this.swipeOutBounded = Math.abs($this.startX - $this.currentX) > swipeOutBounded &&
Math.abs($this.startY - $this.currentY) > swipeOutBounded;
// only trigger `rollover` event if cursor actually moved over this element
if(hasEvent(this, 'rollover') && movedAgain){

// throttle the `rollover` event based on `rollOverFrequency`
var now = event.timeStamp;
var throttle = $this.options.rollOverFrequency;
if ($this.touchRollTime == null || now > ($this.touchRollTime + throttle)){
$this.touchRollTime = now;

triggerEvent(event, this, 'rollover');
}
}

if($this.touchStarted && $this.touchMoved && movedAgain){
triggerEvent(event, this, 'drag');
// only trigger `drag` event if cursor actually moved and if we are still dragging this element
if(hasEvent(this, 'drag') && $this.touchStarted && $this.touchMoved && movedAgain){

// throttle the `drag` event based on `dragFrequency`
var now = event.timeStamp;
var throttle = $this.options.dragFrequency;
if ($this.touchDragTime == null || now > ($this.touchDragTime + throttle)){
$this.touchDragTime = now;

triggerEvent(event, this, 'drag');
}
}
}

Expand Down Expand Up @@ -149,18 +186,18 @@ var vueTouchEvents = {
return;
}

// Fix #33, Trigger `end` event when touch stopped
// trigger `end` event when touch stopped
triggerEvent(event, this, 'release');

if (!$this.touchMoved) {
// detect if this is a longTap event or not
if ($this.callbacks.longtap && event.timeStamp - $this.touchStartTime > $this.options.longTapTimeInterval) {
if (hasEvent(this, 'longtap') && event.timeStamp - $this.touchStartTime > $this.options.longTapTimeInterval) {
if (event.cancelable) {
event.preventDefault();
}
triggerEvent(event, this, 'longtap');

} else if ($this.callbacks.hold && touchholdEnd) {
} else if (hasEvent(this, 'hold') && touchholdEnd) {
if (event.cancelable) {
event.preventDefault();
}
Expand All @@ -170,7 +207,8 @@ var vueTouchEvents = {
triggerEvent(event, this, 'tap');
}

} else if (!$this.swipeOutBounded) {
// performance: only process swipe events if `swipe.*` event is registered on this element
} else if ($this.hasSwipe && !$this.swipeOutBounded) {
var swipeOutBounded = $this.options.swipeTolerance,
direction,
distanceY = Math.abs($this.startY - $this.currentY),
Expand All @@ -184,7 +222,7 @@ var vueTouchEvents = {
}

// Only emit the specified event when it has modifiers
if ($this.callbacks['swipe.' + direction]) {
if (hasEvent(this, 'swipe.' + direction)) {
triggerEvent(event, this, 'swipe.' + direction, direction);
} else {
// Emit a common event when it has no any modifier
Expand All @@ -202,15 +240,23 @@ var vueTouchEvents = {
removeTouchClass(this);
}

function hasEvent($el, eventType) {
var callbacks = $el.$$touchObj.callbacks[eventType];
return (callbacks != null && callbacks.length > 0);
}

function triggerEvent(e, $el, eventType, param) {
var $this = $el.$$touchObj;

// get the callback list
var callbacks = $this.callbacks[eventType] || null;
// get the subscribers for this event
var callbacks = $this.callbacks[eventType];

// exit if no subscribers to this particular event
if (callbacks == null || callbacks.length === 0) {
return null;
}

// per callback
for (var i = 0; i < callbacks.length; i++) {
var binding = callbacks[i];

Expand Down

0 comments on commit 0dd29b3

Please sign in to comment.