diff --git a/dist/ember-touch.js b/dist/ember-touch.js index 4722ea5..a8f3da3 100644 --- a/dist/ember-touch.js +++ b/dist/ember-touch.js @@ -1298,7 +1298,7 @@ Ember.Application.initializer({ gestureManager.registerGesture('swipe', Em.SwipeGestureRecognizer); gestureManager.registerGesture('tap', Em.TapGestureRecognizer); gestureManager.registerGesture('touchHold', Em.TouchHoldGestureRecognizer); - + gestureManager.registerGesture('rotate', Em.RotateGestureRecognizer); } }); @@ -2358,6 +2358,157 @@ Em.SwipeGestureRecognizer = Em.Gesture.extend({ +(function() { +var get = Em.get, set = Em.set; + +/** + @module ember + @submodule ember-touch + */ +/** + Recognizes a rotate gesture. Rotations are continuous gestures that will get fired on a view. The rotation gesture + tracks the changes in the angle between two fingers. + + var myview = Em.View.create({ + + rotateStart: function(recognizer, evt) { + + }, + // usually, you will only use this method + rotateChange: function(recognizer, evt) { + var rotationDegrees = recognizer.get('rotationDegrees'), // total rotation in degrees since gesture start + rotation = recognizer.get('rotation'), // total rotation in radians since gesture start + deltaDegrees = recognizer.get('deltaDegrees'), // rotation in degrees since previous call to rotateChange + delta = recognizer.get('delta'); // rotation in radians since previous call to rotateChange + + this.$().css('rotation', rotationDegrees + 'deg'); + }, + rotateEnd: function(recognizer, evt) { + + }, + rotateCancel: function(recognizer, evt) { + + } +}); + + @class RotateGestureRecognizer + @namespace Ember + @extends Em.Gesture + */ + +Em.RotateGestureRecognizer = Em.Gesture.extend({ + /** + total rotation in radians since gesture start + + @type Number + */ + rotation: 0, + + /** + total rotation in degrees since gesture start + + @type Number + */ + rotationDegrees: 0, + + /** + rotation in radians since previous call to rotateChange + + @type Number + */ + delta: 0, + + /** + rotation in radians since previous call to rotateChange + + @type Number + */ + deltaDegrees: 0, + + numberOfRequiredTouches: 2, + + /** + @private + */ + DEGREES_PER_RADIAN: 180 / Math.PI, + + /** + @private + */ + startAngle: 0, + /** + @private + */ + previousAngle: 0, + /** + @private + */ + previousDelta: 0, + + /** + @private + */ + angle: 0, + + + didBecomePossible: function () { + var angle = this.angleBetweenTouches(); + + set(this, 'startAngle', angle); + set(this, 'angle', angle); + set(this, 'previousAngle', angle); + set(this, 'previousDelta', 0); + set(this, 'delta', 0); + + this.updateAngles(this.angleBetweenTouches()); + }, + shouldBegin: function () { + return true; + }, + didChange: function () { + this.updateAngles(this.angleBetweenTouches()); + }, + eventWasRejected: function () { + var previousDelta = get(this, 'previousDelta'), + previousAngle = get(this, 'previousAngle'); + + this.updateAngles(previousAngle); + + set(this, 'delta', previousDelta); + set(this, 'deltaDegrees', previousDelta * this.DEGREES_PER_RADIAN); + }, + angleBetweenTouches: function () { + var touches = get(this.touches, 'touches'), + dx = touches[1].pageX - touches[0].pageX, + dy = touches[1].pageY - touches[0].pageY; + + return Math.atan2(dy, dx); + }, + updateAngles: function (angle) { + var startAngle = get(this, 'startAngle'), + previousAngle = get(this, 'angle'), + previousDelta = get(this, 'delta'), + rotation = startAngle - angle, + delta = previousAngle - angle; + + set(this, 'previousAngle', previousAngle); + set(this, 'previousDelta', previousDelta); + set(this, 'angle', angle); + set(this, 'rotation', rotation); + set(this, 'rotationDegrees', rotation * this.DEGREES_PER_RADIAN); + set(this, 'delta', delta); + set(this, 'deltaDegrees', delta * this.DEGREES_PER_RADIAN); + }, + toString: function () { + return Em.RotateGestureRecognizer + '<' + Em.guidFor(this) + '>'; + } +}); + + +})(); + + + (function() { })(); diff --git a/packages/ember-touch/lib/gesture_recognizers.js b/packages/ember-touch/lib/gesture_recognizers.js index bbbb498..9a85a8c 100644 --- a/packages/ember-touch/lib/gesture_recognizers.js +++ b/packages/ember-touch/lib/gesture_recognizers.js @@ -4,3 +4,4 @@ require('ember-touch/gesture_recognizers/tap'); require('ember-touch/gesture_recognizers/press'); require('ember-touch/gesture_recognizers/touch_hold'); require('ember-touch/gesture_recognizers/swipe'); +require('ember-touch/gesture_recognizers/rotate'); diff --git a/packages/ember-touch/lib/gesture_recognizers/rotate.js b/packages/ember-touch/lib/gesture_recognizers/rotate.js new file mode 100644 index 0000000..f476b92 --- /dev/null +++ b/packages/ember-touch/lib/gesture_recognizers/rotate.js @@ -0,0 +1,147 @@ +require('ember-touch/system/gesture'); + +var get = Em.get, set = Em.set; + +/** + @module ember + @submodule ember-touch + */ +/** + Recognizes a rotate gesture. Rotations are continuous gestures that will get fired on a view. The rotation gesture + tracks the changes in the angle between two fingers. + + var myview = Em.View.create({ + + rotateStart: function(recognizer, evt) { + + }, + // usually, you will only use this method + rotateChange: function(recognizer, evt) { + var rotationDegrees = recognizer.get('rotationDegrees'), // total rotation in degrees since gesture start + rotation = recognizer.get('rotation'), // total rotation in radians since gesture start + deltaDegrees = recognizer.get('deltaDegrees'), // rotation in degrees since previous call to rotateChange + delta = recognizer.get('delta'); // rotation in radians since previous call to rotateChange + + this.$().css('rotation', rotationDegrees + 'deg'); + }, + rotateEnd: function(recognizer, evt) { + + }, + rotateCancel: function(recognizer, evt) { + + } +}); + + @class RotateGestureRecognizer + @namespace Ember + @extends Em.Gesture + */ + +Em.RotateGestureRecognizer = Em.Gesture.extend({ + /** + total rotation in radians since gesture start + + @type Number + */ + rotation: 0, + + /** + total rotation in degrees since gesture start + + @type Number + */ + rotationDegrees: 0, + + /** + rotation in radians since previous call to rotateChange + + @type Number + */ + delta: 0, + + /** + rotation in radians since previous call to rotateChange + + @type Number + */ + deltaDegrees: 0, + + numberOfRequiredTouches: 2, + + /** + @private + */ + DEGREES_PER_RADIAN: 180 / Math.PI, + + /** + @private + */ + startAngle: 0, + /** + @private + */ + previousAngle: 0, + /** + @private + */ + previousDelta: 0, + + /** + @private + */ + angle: 0, + + + didBecomePossible: function () { + var angle = this.angleBetweenTouches(); + + set(this, 'startAngle', angle); + set(this, 'angle', angle); + set(this, 'previousAngle', angle); + set(this, 'previousDelta', 0); + set(this, 'delta', 0); + + this.updateAngles(this.angleBetweenTouches()); + }, + shouldBegin: function () { + return true; + }, + didChange: function () { + this.updateAngles(this.angleBetweenTouches()); + }, + eventWasRejected: function () { + var previousDelta = get(this, 'previousDelta'), + previousAngle = get(this, 'previousAngle'); + + this.updateAngles(previousAngle); + + set(this, 'delta', previousDelta); + set(this, 'deltaDegrees', previousDelta * this.DEGREES_PER_RADIAN); + }, + angleBetweenTouches: function () { + var touches = get(this.touches, 'touches'), + dx = touches[1].pageX - touches[0].pageX, + dy = touches[1].pageY - touches[0].pageY; + + return Math.atan2(dy, dx); + }, + updateAngles: function (angle) { + var startAngle = get(this, 'startAngle'), + previousAngle = get(this, 'angle'), + previousDelta = get(this, 'delta'), + rotation = startAngle - angle, + delta = previousAngle - angle; + + set(this, 'previousAngle', previousAngle); + set(this, 'previousDelta', previousDelta); + set(this, 'angle', angle); + set(this, 'rotation', rotation); + set(this, 'rotationDegrees', rotation * this.DEGREES_PER_RADIAN); + set(this, 'delta', delta); + set(this, 'deltaDegrees', delta * this.DEGREES_PER_RADIAN); + }, + toString: function () { + return Em.RotateGestureRecognizer + '<' + Em.guidFor(this) + '>'; + } +}); + diff --git a/packages/ember-touch/lib/system/application_ext.js b/packages/ember-touch/lib/system/application_ext.js index 1e0293e..f39539b 100644 --- a/packages/ember-touch/lib/system/application_ext.js +++ b/packages/ember-touch/lib/system/application_ext.js @@ -36,7 +36,7 @@ Ember.Application.initializer({ gestureManager.registerGesture('swipe', Em.SwipeGestureRecognizer); gestureManager.registerGesture('tap', Em.TapGestureRecognizer); gestureManager.registerGesture('touchHold', Em.TouchHoldGestureRecognizer); - + gestureManager.registerGesture('rotate', Em.RotateGestureRecognizer); } }); diff --git a/packages/ember-touch/tests/gesture_recognizers/rotate_test.js b/packages/ember-touch/tests/gesture_recognizers/rotate_test.js new file mode 100644 index 0000000..91aac6e --- /dev/null +++ b/packages/ember-touch/tests/gesture_recognizers/rotate_test.js @@ -0,0 +1,233 @@ +var set = Em.set; +var get = Em.get; + +var view, View, change; +var application; +var numEnded = 0; + +module('Rotate Test',{ + setup: function() { + numEnded = 0; + + View = Em.View.extend({ + elementId: 'gestureTest', + rotateStart: function(recognizer) { + change = get(recognizer, 'deltaDegrees'); + + return Math.abs(change) >= 45; + }, + rotateChange: function(recognizer) { + change = get(recognizer, 'deltaDegrees'); + + return true; + }, + rotateEnd: function(recognizer) { + numEnded++; + return true; + } + }); + + Em.run(function(){ + application = Em.Application.create({ + ready: function() { + view = View.create({}); + view.append(); + start(); + } + }); + stop(); + }); + }, + + teardown: function() { + var touchEvent = new jQuery.Event(); + touchEvent.type='touchend'; + touchEvent['originalEvent'] = { + changedTouches: [] + }; + view.$().trigger(touchEvent); + + Em.run(function(){ + view.destroy(); + application.destroy(); + }); + } +}); + +test('one start event should put it in waiting state', function() { + var touchEvent = new jQuery.Event(); + + touchEvent.type='touchstart'; + touchEvent['originalEvent'] = { + targetTouches: [{ + pageX: 0, + pageY: 10 + }] + }; + + view.$().trigger(touchEvent); + + var gestures = get(get(view, 'eventManager'), 'gestures'); + + ok(gestures); + equal(gestures.length,1); + equal(get(gestures[0], 'state'), Em.Gesture.WAITING_FOR_TOUCHES, 'gesture should be waiting'); +}); + +test('two start events should put it in possible state', function() { + var touchEvent = new jQuery.Event(); + + touchEvent.type='touchstart'; + touchEvent['originalEvent'] = { + targetTouches: [{ + identifier: 0, + pageX: 0, + pageY: 10 + }, + { + identifier: 1, + pageX: 10, + pageY: 10 + }] + }; + + view.$().trigger(touchEvent); + + var gestures = get(get(view, 'eventManager'), 'gestures'); + + ok(gestures); + equal(gestures.length,1); + equal(get(gestures[0], 'state'),Em.Gesture.POSSIBLE, 'gesture should be possible'); +}); + +test('If the touches move, the rotation should reflect the change', function() { + var touchEvent = new jQuery.Event(); + touchEvent.type='touchstart'; + touchEvent['originalEvent'] = { + targetTouches: [{ + identifier: 0, + pageX: 0, + pageY: 0 + }, + { + identifier: 1, + pageX: 0, + pageY: 10 + }] + }; + + view.$().trigger(touchEvent); + + var gestures = get(get(view, 'eventManager'), 'gestures'); + + ok(gestures); + equal(gestures.length,1); + equal(get(gestures[0], 'state'),Em.Gesture.POSSIBLE, 'gesture should be possible'); + + touchEvent = new jQuery.Event(); + touchEvent.type='touchmove'; + touchEvent['originalEvent'] = { + changedTouches: [{ + identifier: 1, + pageX: 10, + pageY: 0 + }] + }; + + view.$().trigger(touchEvent); + + gestures = get(get(view, 'eventManager'), 'gestures'); + equal(get(gestures[0], 'state'), Em.Gesture.BEGAN, 'gesture should be began'); + + equal(get(gestures[0], 'rotationDegrees'), 90, 'rotation should be 90 degrees'); + equal(get(gestures[0], 'rotation'), Math.PI / 2, 'rotation should be 0.5 PI radians'); + equal(get(gestures[0], 'deltaDegrees'), 90, 'delta should be 90 degrees'); + equal(get(gestures[0], 'delta'), Math.PI / 2, 'delta should be 0.5 PI radians'); + + touchEvent = new jQuery.Event(); + touchEvent.type='touchmove'; + touchEvent['originalEvent'] = { + changedTouches: [{ + identifier: 1, + pageX: 0, + pageY: 10 + }] + }; + + view.$().trigger(touchEvent); + + gestures = get(get(view, 'eventManager'), 'gestures'); + equal(get(gestures[0], 'state'),Em.Gesture.CHANGED, 'gesture should be changed'); + + equal(get(gestures[0], 'rotationDegrees'), 0, 'rotation should be back to 0 degrees'); + equal(get(gestures[0], 'rotation'), 0, 'rotation should be back to 0 radians'); + equal(get(gestures[0], 'deltaDegrees'), -90, 'delta should be -90 degrees'); + equal(get(gestures[0], 'delta'), -0.5 * Math.PI, 'delta should be -0.5 PI radians'); + + touchEvent = new jQuery.Event(); + touchEvent.type='touchend'; + touchEvent['originalEvent'] = { + changedTouches: [{ + identifier: 0, + pageX: 10, + pageY: 20 + }] + }; + + view.$().trigger(touchEvent); + + touchEvent = new jQuery.Event(); + touchEvent.type='touchend'; + touchEvent['originalEvent'] = { + changedTouches: [{ + identifier: 1, + pageX: 10, + pageY: 20 + }] + }; + + view.$().trigger(touchEvent); + + gestures = get(get(view, 'eventManager'), 'gestures'); + equal(get(gestures[0], 'state'),Em.Gesture.ENDED, 'gesture should be ended'); + + equal(numEnded, 1, 'rotateEnd should be called once'); +}); + +test('If a gesture event returns false, reject the change', function() { + var touchEvent = jQuery.Event('touchstart'); + touchEvent['originalEvent'] = { + targetTouches: [{ + identifier: 0, + pageX: 0, + pageY: 0 + }, + { + identifier: 1, + pageX: 0, + pageY: 10 + }] + }; + + view.$().trigger(touchEvent); + + var gestures = get(get(view, 'eventManager'), 'gestures'); + equal(get(gestures[0], 'rotationDegrees'), 0, 'start at zero'); + + touchEvent = jQuery.Event('touchmove'); + touchEvent['originalEvent'] = { + changedTouches: [{ + identifier: 1, + pageX: 0.1, + pageY: 10 + }] + }; + + view.$().trigger(touchEvent); + + gestures = get(get(view, 'eventManager'), 'gestures'); + equal(get(gestures[0], 'rotationDegrees'), 0, 'rotation degrees should not change'); + equal(get(gestures[0], 'rotation'), 0, 'rotation radians should not change'); + equal(get(gestures[0], 'deltaDegrees'), 0, 'delta degrees should not change'); + equal(get(gestures[0], 'delta'), 0, 'delta radians should not change'); +});