| Index: client/touch/TouchHandler.dart
|
| ===================================================================
|
| --- client/touch/TouchHandler.dart (revision 4144)
|
| +++ client/touch/TouchHandler.dart (working copy)
|
| @@ -1,393 +0,0 @@
|
| -// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -/**
|
| - * Touch Handler. Class that handles all touch events and
|
| - * uses them to interpret higher level gestures and behaviors. TouchEvent is a
|
| - * built in mobile safari type:
|
| - * [http://developer.apple.com/safari/library/documentation/UserExperience/Reference/TouchEventClassReference/TouchEvent/TouchEvent.html].
|
| - *
|
| - * Examples of higher level gestures this class is intended to support
|
| - * - click, double click, long click
|
| - * - dragging, swiping, zooming
|
| - *
|
| - * Touch Behavior:
|
| - * Use this class to make your elements 'touchable' (see Touchable.dart).
|
| - * Intended to work with all webkit browsers.
|
| - *
|
| - * Drag Behavior:
|
| - * Use this class to make your elements 'draggable' (see draggable.js).
|
| - * This behavior will handle all of the required events and report the
|
| - * properties of the drag to you while the touch is happening and at the
|
| - * end of the drag sequence. This behavior will NOT perform the actual
|
| - * dragging (redrawing the element) for you, this responsibility is left to
|
| - * the client code. This behavior contains a work around for a mobile
|
| - * safari bug where the 'touchend' event is not dispatched when the touch
|
| - * goes past the bottom of the browser window.
|
| - * This is intended to work well in iframes.
|
| - * Intended to work with all webkit browsers, tested only on iPhone 3.x so
|
| - * far.
|
| - *
|
| - * Click Behavior:
|
| - * Not yet implemented.
|
| - *
|
| - * Zoom Behavior:
|
| - * Not yet implemented.
|
| - *
|
| - * Swipe Behavior:
|
| - * Not yet implemented.
|
| - */
|
| -class TouchHandler {
|
| - Touchable _touchable;
|
| - Element _element;
|
| -
|
| - /** The absolute sum of all touch y deltas. */
|
| - int _totalMoveY;
|
| -
|
| - /** The absolute sum of all touch x deltas. */
|
| - int _totalMoveX;
|
| -
|
| - /**
|
| - * A list of tuples where the first item is the horizontal component of a
|
| - * recent relevant touch and the second item is the touch's time stamp. Old
|
| - * touches are removed based on the max tracking time and when direction
|
| - * changes.
|
| - */
|
| - List<int> _recentTouchesX;
|
| -
|
| - /**
|
| - * A list of tuples where the first item is the vertical component of a
|
| - * recent relevant touch and the second item is the touch's time stamp. Old
|
| - * touches are removed based on the max tracking time and when direction
|
| - * changes.
|
| - */
|
| - List<int> _recentTouchesY;
|
| -
|
| - // TODO(jacobr): make customizable by passing optional parameters to the
|
| - // TouchHandler constructor.
|
| - /**
|
| - * Minimum movement of touch required to be considered a drag.
|
| - */
|
| - static final _MIN_TRACKING_FOR_DRAG = 2;
|
| -
|
| - /**
|
| - * The maximum number of ms to track a touch event. After an event is older
|
| - * than this value, it will be ignored in velocity calculations.
|
| - */
|
| - static final _MAX_TRACKING_TIME = 250;
|
| -
|
| - /** The maximum number of touches to track. */
|
| - static final _MAX_TRACKING_TOUCHES = 5;
|
| -
|
| - /**
|
| - * The maximum velocity to return, in pixels per millisecond, that is used to
|
| - * guard against errors in calculating end velocity of a drag. This is a very
|
| - * fast drag velocity.
|
| - */
|
| - static final _MAXIMUM_VELOCITY = 5;
|
| -
|
| - /**
|
| - * The velocity to return, in pixel per millisecond, when the time stamps on
|
| - * the events are erroneous. The browser can return bad time stamps if the
|
| - * thread is blocked for the duration of the drag. This is a low velocity to
|
| - * prevent the content from moving quickly after a slow drag. It is less
|
| - * jarring if the content moves slowly after a fast drag.
|
| - */
|
| - static final _VELOCITY_FOR_INCORRECT_EVENTS = 1;
|
| -
|
| - Draggable _draggable;
|
| - bool _tracking;
|
| - bool _dragging;
|
| - bool _touching;
|
| - int _startTouchX;
|
| - int _startTouchY;
|
| - int _startTime;
|
| - TouchEvent _lastEvent;
|
| - int _lastTouchX;
|
| - int _lastTouchY;
|
| - int _lastMoveX;
|
| - int _lastMoveY;
|
| - int _endTime;
|
| - int _endTouchX;
|
| - int _endTouchY;
|
| -
|
| - TouchHandler(Touchable touchable, [Element element = null])
|
| - : _touchable = touchable,
|
| - _totalMoveY = 0,
|
| - _totalMoveX = 0,
|
| - _recentTouchesX = new List<int>(),
|
| - _recentTouchesY = new List<int>(),
|
| - // TODO(jmesserly): I don't like having to initialize all booleans here
|
| - // See b/5045736
|
| - _dragging = false,
|
| - _tracking = false,
|
| - _touching = false {
|
| - _element = element != null ? element : touchable.getElement();
|
| - }
|
| -
|
| - /**
|
| - * Begin tracking the touchable element, it is eligible for dragging.
|
| - */
|
| - void _beginTracking() {
|
| - _tracking = true;
|
| - }
|
| -
|
| - /**
|
| - * Stop tracking the touchable element, it is no longer dragging.
|
| - */
|
| - void _endTracking() {
|
| - _tracking = false;
|
| - _dragging = false;
|
| - _totalMoveY = 0;
|
| - _totalMoveX = 0;
|
| - }
|
| -
|
| - /**
|
| - * Correct erroneous velocities by capping the velocity if we think it's too
|
| - * high, or setting it to a default velocity if know that the event data is
|
| - * bad. Returns the corrected velocity.
|
| - */
|
| - num _correctVelocity(num velocity) {
|
| - num absVelocity = velocity.abs();
|
| - if (absVelocity > _MAXIMUM_VELOCITY) {
|
| - absVelocity = _recentTouchesY.length < 6 ?
|
| - _VELOCITY_FOR_INCORRECT_EVENTS : _MAXIMUM_VELOCITY;
|
| - }
|
| - return absVelocity * (velocity < 0 ? -1 : 1);
|
| - }
|
| -
|
| - /**
|
| - * Start listenting for events.
|
| - * If [capture] is True the TouchHandler should listen during the capture
|
| - * phase.
|
| - */
|
| - void enable([bool capture = false]) {
|
| - Function onEnd = (e) { _onEnd(e.timeStamp, e); };
|
| - _addEventListeners(
|
| - _element,
|
| - (e) { _onStart(e); },
|
| - (e) { _onMove(e); }, onEnd, onEnd, capture);
|
| - }
|
| -
|
| - /**
|
| - * Get the current horizontal drag delta. Drag delta is defined as the deltaX
|
| - * of the start touch position and the last touch position.
|
| - */
|
| - int getDragDeltaX() {
|
| - return _lastTouchX - _startTouchX;
|
| - }
|
| -
|
| - /**
|
| - * Get the current vertical drag delta. Drag delta is defined as the deltaY of
|
| - * the start touch position and the last touch position.
|
| - */
|
| - int getDragDeltaY() {
|
| - return _lastTouchY - _startTouchY;
|
| - }
|
| -
|
| - /**
|
| - * Get end velocity of the drag. This method is specific to drag behavior, so
|
| - * if touch behavior and drag behavior is split then this should go with drag
|
| - * behavior. End velocity is defined as deltaXY / deltaTime where deltaXY is
|
| - * the difference between endPosition and the oldest recent position, and
|
| - * deltaTime is the difference between endTime and the oldest recent time
|
| - * stamp.
|
| - */
|
| - Coordinate getEndVelocity() {
|
| - num velocityX = 0;
|
| - num velocityY = 0;
|
| -
|
| - if (_recentTouchesX.length > 0) {
|
| - num timeDeltaX = Math.max(1, _endTime - _recentTouchesX[1]);
|
| - velocityX = (_endTouchX - _recentTouchesX[0]) / timeDeltaX;
|
| - }
|
| -
|
| - if (_recentTouchesY.length > 0) {
|
| - num timeDeltaY = Math.max(1, _endTime - _recentTouchesY[1]);
|
| - velocityY = (_endTouchY - _recentTouchesY[0]) / timeDeltaY;
|
| - }
|
| - velocityX = _correctVelocity(velocityX);
|
| - velocityY = _correctVelocity(velocityY);
|
| - return new Coordinate(velocityX, velocityY);
|
| - }
|
| -
|
| - /**
|
| - * Return the touch of the last event.
|
| - */
|
| - Touch _getLastTouch() {
|
| - assert (_lastEvent != null); // Last event not set
|
| - return _lastEvent.touches[0];
|
| - }
|
| -
|
| - /**
|
| - * Is the touch manager currently tracking touch moves to detect a drag?
|
| - */
|
| - bool isTracking() {
|
| - return _tracking;
|
| - }
|
| -
|
| - /**
|
| - * Touch end handler.
|
| - */
|
| - void _onEnd(int timeStamp, [TouchEvent e = null]) {
|
| - _touching = false;
|
| - _touchable.onTouchEnd();
|
| - if (!_tracking || _draggable === null) {
|
| - return;
|
| - }
|
| - Touch touch = _getLastTouch();
|
| - int clientX = touch.clientX;
|
| - int clientY = touch.clientY;
|
| - if (_dragging) {
|
| - _endTime = timeStamp;
|
| - _endTouchX = clientX;
|
| - _endTouchY = clientY;
|
| - _recentTouchesX = _removeOldTouches(_recentTouchesX, timeStamp);
|
| - _recentTouchesY = _removeOldTouches(_recentTouchesY, timeStamp);
|
| - _draggable.onDragEnd();
|
| - if (e !== null) {
|
| - e.preventDefault();
|
| - }
|
| - ClickBuster.preventGhostClick(_startTouchX, _startTouchY);
|
| - }
|
| - _endTracking();
|
| - }
|
| -
|
| - /**
|
| - * Touch move handler.
|
| - */
|
| - void _onMove(TouchEvent e) {
|
| - if (!_tracking || _draggable === null) {
|
| - return;
|
| - }
|
| - final touch = e.touches[0];
|
| - int clientX = touch.clientX;
|
| - int clientY = touch.clientY;
|
| - int moveX = _lastTouchX - clientX;
|
| - int moveY = _lastTouchY - clientY;
|
| - _totalMoveX += moveX.abs();
|
| - _totalMoveY += moveY.abs();
|
| - _lastTouchX = clientX;
|
| - _lastTouchY = clientY;
|
| - if (!_dragging &&
|
| - ((_totalMoveY > _MIN_TRACKING_FOR_DRAG && _draggable.verticalEnabled) ||
|
| - (_totalMoveX > _MIN_TRACKING_FOR_DRAG &&
|
| - _draggable.horizontalEnabled))) {
|
| - _dragging = _draggable.onDragStart(e);
|
| - if (!_dragging) {
|
| - _endTracking();
|
| - } else {
|
| - _startTouchX = clientX;
|
| - _startTouchY = clientY;
|
| - _startTime = e.timeStamp;
|
| - }
|
| - }
|
| - if (_dragging) {
|
| - _draggable.onDragMove();
|
| - _lastEvent = e;
|
| - e.preventDefault();
|
| - _recentTouchesX =
|
| - _removeTouchesInWrongDirection(_recentTouchesX, _lastMoveX, moveX);
|
| - _recentTouchesY =
|
| - _removeTouchesInWrongDirection(_recentTouchesY, _lastMoveY, moveY);
|
| - _recentTouchesX = _removeOldTouches(_recentTouchesX, e.timeStamp);
|
| - _recentTouchesY = _removeOldTouches(_recentTouchesY, e.timeStamp);
|
| - _recentTouchesX.add(clientX);
|
| - _recentTouchesX.add(e.timeStamp);
|
| - _recentTouchesY.add(clientY);
|
| - _recentTouchesY.add(e.timeStamp);
|
| - }
|
| - _lastMoveX = moveX;
|
| - _lastMoveY = moveY;
|
| - }
|
| -
|
| - /**
|
| - * Touch start handler.
|
| - */
|
| - void _onStart(TouchEvent e) {
|
| - if (_touching) {
|
| - return;
|
| - }
|
| - _touching = true;
|
| - if (!_touchable.onTouchStart(e) || _draggable === null) {
|
| - return;
|
| - }
|
| - final touch = e.touches[0];
|
| - _startTouchX = _lastTouchX = touch.clientX;
|
| - _startTouchY = _lastTouchY = touch.clientY;
|
| - _startTime = e.timeStamp;
|
| - // TODO(jacobr): why don't we just clear the lists?
|
| - _recentTouchesX = new List<int>();
|
| - _recentTouchesY = new List<int>();
|
| - _recentTouchesX.add(touch.clientX);
|
| - _recentTouchesX.add(e.timeStamp);
|
| - _recentTouchesY.add(touch.clientY);
|
| - _recentTouchesY.add(e.timeStamp);
|
| - _lastEvent = e;
|
| - _beginTracking();
|
| - }
|
| -
|
| - /**
|
| - * Filters the provided recent touches list to remove all touches older than
|
| - * the max tracking time or the 5th most recent touch.
|
| - * [recentTouches] specifies a list of tuples where the first item is the x
|
| - * or y component of the recent touch and the second item is the touch time
|
| - * stamp. The time of the most recent event is specified by [recentTime].
|
| - */
|
| - List<int> _removeOldTouches(List<int> recentTouches,
|
| - int recentTime) {
|
| - int count = 0;
|
| - final len = recentTouches.length;
|
| - assert (len % 2 == 0);
|
| - while (count < len &&
|
| - recentTime - recentTouches[count + 1] > _MAX_TRACKING_TIME ||
|
| - (len - count) > _MAX_TRACKING_TOUCHES * 2) {
|
| - count += 2;
|
| - }
|
| - return count == 0 ? recentTouches : _removeFirstN(recentTouches, count);
|
| - }
|
| -
|
| - static List<int> _removeFirstN(List<int> list, int n) {
|
| - return list.getRange(n, list.length - n);
|
| - }
|
| -
|
| - /**
|
| - * Filters the provided recent touches list to remove all touches except the
|
| - * last if the move direction has changed.
|
| - * [recentTouches] specifies a list of tuples where the first item is the x
|
| - * or y component of the recent touch and the second item is the touch time
|
| - * stamp. The x or y component of the most recent move is specified by
|
| - * [recentMove].
|
| - */
|
| - List<int> _removeTouchesInWrongDirection(List<int> recentTouches,
|
| - int lastMove, int recentMove) {
|
| - if (lastMove !=0 && recentMove != 0 && recentTouches.length > 2 &&
|
| - _xor(lastMove > 0, recentMove > 0)) {
|
| - return _removeFirstN(recentTouches, recentTouches.length - 2);
|
| - }
|
| - return recentTouches;
|
| - }
|
| -
|
| - // TODO(jacobr): why doesn't bool implement the xor operator directly?
|
| - static bool _xor(bool a, bool b) {
|
| - return (a === true || b === true) && !(a === true && b === true);
|
| - }
|
| -
|
| - /**
|
| - * Reset the touchable element.
|
| - */
|
| - void reset() {
|
| - _endTracking();
|
| - _touching = false;
|
| - }
|
| -
|
| - /**
|
| - * Call this method to enable drag behavior on a draggable delegate.
|
| - * The [draggable] object can be the same as the [_touchable] object, they are
|
| - * assigned to different members to allow for strong typing with interfaces.
|
| - */
|
| - void setDraggable(Draggable draggable) {
|
| - _draggable = draggable;
|
| - }
|
| -}
|
|
|