Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
feat: New touch module including ng-swipe-left/ng-swipe-right directives
Browse files Browse the repository at this point in the history
Support swipe gesture for touch enabled devices.

Closes #1403
  • Loading branch information
mehmetf authored and vsavkin committed Sep 14, 2014
1 parent 29f3947 commit 5d84c6d
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 0 deletions.
27 changes: 27 additions & 0 deletions lib/touch/module.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Touch related functionality for AngularDart apps.
*
* To use, install the TouchModule into your main module:
*
* var module = new Module()
* ..install(new TouchModule());
*
* Once the module is installed, you can use decorators such
* as ng-swipe-left or ng-swipe right
*/

library angular.touch;

import 'dart:html' as dom;
import 'package:di/di.dart';
import 'package:angular/core/annotation.dart';

part 'ng_swipe.dart';

class TouchModule extends Module {
TouchModule() {
bind(NgSwipeLeft, toValue: null);
bind(NgSwipeRight, toValue: null);
}
}

106 changes: 106 additions & 0 deletions lib/touch/ng_swipe.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
part of angular.touch;

/**
* Base class for Swipe Gesture. Decides whether a swipe is performed
* and gives the x, y direction of the swipe.
*/
abstract class _SwipeGesture {
static const int NO_SWIPE = -1;
static const int DOWN = 0;
static const int UP = 1;
static const int LEFT = 2;
static const int RIGHT = 3;

// Less than 100 pixels of move in each direction does not count.
static const int _POS_TOLERANCE = 100;
// If swipe lasts more than 1s, it is not a swipe.
static const int _TIME_TOLERANCE = 1000;

// Function to be called on swipe.
Function fn;

// Subclasses decide on swipe direction whether to call fn or not.
bool get shouldFire;

int _startX;
int _startY;
int _startTime;
int xDirection;
int yDirection;

_SwipeGesture(dom.Element target) {
target.onTouchStart.listen(_handleTouchStartEvent);
target.onTouchEnd.listen(_handleTouchEndEvent);
}

void handleTouchStart(int x, int y, int timestamp) {
// Reset values every time swipe starts
xDirection = NO_SWIPE;
yDirection = NO_SWIPE;
_startX = x;
_startY = y;
_startTime = timestamp;
}

void handleTouchEnd(int x, int y, int timestamp) {
int touchDuration = timestamp - _startTime;
if (touchDuration > _TIME_TOLERANCE) {
return;
}
if (y > _startY + _POS_TOLERANCE) {
yDirection = DOWN;
} else if (y < _startY - _POS_TOLERANCE) {
yDirection = UP;
}
if (x > _startX + _POS_TOLERANCE) {
xDirection = RIGHT;
} else if (x < _startX - _POS_TOLERANCE) {
xDirection = LEFT;
}
if (fn != null && shouldFire) {
fn();
}
}

void _handleTouchStartEvent(dom.TouchEvent ev) {
// Guaranteed to have at least one touch in changedTouches.
dom.Touch t = ev.changedTouches.first;
handleTouchStart(t.client.x, t.client.y, ev.timeStamp);
}

void _handleTouchEndEvent(dom.TouchEvent ev) {
// Guaranteed to have at least one touch in changedTouches.
dom.Touch t = ev.changedTouches.first;
handleTouchEnd(t.client.x, t.client.y, ev.timeStamp);
}
}

/**
* The `ng-swipe-right` directive allows execution of callbacks when user
* swipes her finger to the right.
* Also see [NgSwipeLeft].
*/
@Decorator(
selector: '[ng-swipe-right]',
map: const {'ng-swipe-right':'&fn'})
class NgSwipeRight extends _SwipeGesture {
NgSwipeRight(dom.Element target): super(target);

bool get shouldFire => xDirection == _SwipeGesture.RIGHT &&
yDirection == _SwipeGesture.NO_SWIPE;
}

/**
* The `ng-swipe-left` directive allows execution of callbacks when user
* swipes his finger to the left.
* Also see [NgSwipeRight].
*/
@Decorator(
selector: '[ng-swipe-left]',
map: const {'ng-swipe-left':'&fn'})
class NgSwipeLeft extends _SwipeGesture {
NgSwipeLeft(dom.Element target): super(target);

bool get shouldFire => xDirection == _SwipeGesture.LEFT &&
yDirection == _SwipeGesture.NO_SWIPE;
}
1 change: 1 addition & 0 deletions test/_specs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export 'package:angular/directive/module.dart';
export 'package:angular/formatter/module.dart';
export 'package:angular/routing/module.dart';
export 'package:angular/animate/module.dart';
export 'package:angular/touch/module.dart';
export 'package:angular/mock/module.dart';
export 'package:perf_api/perf_api.dart';

Expand Down
17 changes: 17 additions & 0 deletions test/angular_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ main() {
assertSymbolNamesAreOk(ALLOWED_NAMES, libraryInfo);

});

it('should not export unknown symbols from touch', () {
LibraryInfo libraryInfo;
try {
libraryInfo = getSymbolsFromLibrary("angular.touch");
} on UnimplementedError catch (e) {
return; // Not implemented, quietly skip.
}

var ALLOWED_NAMES = [
"angular.touch.NgSwipeLeft",
"angular.touch.NgSwipeRight",
"angular.touch.TouchModule"
];
assertSymbolNamesAreOk(ALLOWED_NAMES, libraryInfo);

});

it('should not export unknown symbols from angular', () {
// Test is failing? Add new symbols to the "ALLOWED_NAMES" list below.
Expand Down
71 changes: 71 additions & 0 deletions test/touch/ng_swipe_spec.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
library ng_swipe_spec;

import '../_specs.dart';

/**
* Unfortunately, it is not possible to test swipe using events since
* TouchEvent cannot be constructed using fake dom.Touch elements.
* See: dartbug.com/8314
* TODO(8314): Once this is fixed, should update the tests.
*/
void main() {
describe('ng-swipe-right', () {
NgSwipeRight swipe = new NgSwipeRight(new DivElement());

it('should not fire when distance is not enough', () {
swipe.handleTouchStart(10, 10, 0);
swipe.handleTouchEnd(15, 15, 1);
expect(swipe.shouldFire).toBeFalse();
});

it('should fire on swipe to the right', () {
swipe.handleTouchStart(10, 10, 0);
swipe.handleTouchEnd(130, 15, 1);
expect(swipe.shouldFire).toBeTrue();
});

it('should not fire on swipe to the left', () {
swipe.handleTouchStart(130, 10, 0);
swipe.handleTouchEnd(10, 15, 1);
expect(swipe.shouldFire).toBeFalse();
});

it('should not fire on slow swipe', () {
swipe.handleTouchStart(10, 10, 0);
// 2 seconds later
swipe.handleTouchEnd(130, 15, 2000);
expect(swipe.shouldFire).toBeFalse();
});


});

describe('ng-swipe-left', () {
NgSwipeLeft swipe = new NgSwipeLeft(new DivElement());

it('should not fire when distance is not enough', () {
swipe.handleTouchStart(10, 10, 0);
swipe.handleTouchEnd(15, 15, 1);
expect(swipe.shouldFire).toBeFalse();
});

it('should not fire on swipe to the right', () {
swipe.handleTouchStart(10, 10, 0);
swipe.handleTouchEnd(130, 15, 1);
expect(swipe.shouldFire).toBeFalse();
});

it('should fire on swipe to the left', () {
swipe.handleTouchStart(130, 10, 0);
swipe.handleTouchEnd(10, 15, 1);
expect(swipe.shouldFire).toBeTrue();
});

it('should not fire on swipe on slow swipe', () {
swipe.handleTouchStart(130, 10, 0);
// 2 seconds later
swipe.handleTouchEnd(10, 15, 2000);
expect(swipe.shouldFire).toBeFalse();
});
});
}

0 comments on commit 5d84c6d

Please sign in to comment.