Skip to content

Commit

Permalink
Bugs 951014, 987569, 989361: better orientation and transitions for p…
Browse files Browse the repository at this point in the history
…review gallery and pick confirmation views

don't register multiple resize handlers

restore the start and stop methods to lib/orientation.js

fix background color of preview gallery container

make swipe transition in preview gallery velocity sensitive

update tests

handle review comments

add code to remove resize listener in the confirm module
  • Loading branch information
David Flanagan committed Apr 10, 2014
1 parent 20de510 commit f46815a
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 264 deletions.
13 changes: 5 additions & 8 deletions apps/camera/js/controllers/preview-gallery.js
Expand Up @@ -66,7 +66,7 @@ PreviewGalleryController.prototype.openPreview = function() {
this.view.on('click:share', this.shareCurrentItem);
this.view.on('click:delete', this.deleteCurrentItem);
this.view.on('click:back', this.closePreview);
this.view.on('itemChange', this.handleItemChange);
this.view.on('swipe', this.handleSwipe);

// If lockscreen is locked, hide all control buttons
var secureMode = this.app.inSecureMode;
Expand Down Expand Up @@ -204,15 +204,12 @@ PreviewGalleryController.prototype.updatePreviewGallery = function(index) {
/**
* To Do: Image Swipe Transition
*/
PreviewGalleryController.prototype.handleItemChange = function(e) {
var direction = e.detail.direction;
switch (direction) {
case 'left': // go to next image
PreviewGalleryController.prototype.handleSwipe = function(direction) {
if (direction === 'left') {
this.next();
break;
case 'right': // go to previous
}
else if (direction === 'right') {
this.previous();
break;
}
};

Expand Down
21 changes: 21 additions & 0 deletions apps/camera/js/lib/orientation.js
Expand Up @@ -15,6 +15,25 @@ define(function(require, exports, module) {
current = degrees;
}

// Camera normally has its orientation locked to portrait mode.
// But we unlock orientation when displaying image and video previews.
// When orientation is unlocked, we call listener.stop().
// We calls call stop() when recording a video, and then restart
// when recording is done. If our app ever changes so that we can call
// unlock while the orientation listener is in the stopped state, then
// we would need to modify the lock() function so that it did not
// restart the listener. That is not needed now, however and is omitted.

function unlock() {
screen.mozUnlockOrientation();
listener.stop();
}

function lock() {
screen.mozLockOrientation('portrait-primary');
listener.start();
}

/**
* Exports
*/
Expand All @@ -24,6 +43,8 @@ define(function(require, exports, module) {
off: listener.off,
start: listener.start,
stop: listener.stop,
unlock: unlock,
lock: lock,
get: function() {
return current;
}
Expand Down
122 changes: 30 additions & 92 deletions apps/camera/js/lib/panzoom.js
Expand Up @@ -6,7 +6,6 @@ define(function(require, exports, module) {
*/

var GestureDetector = require('GestureDetector');
var orientation = require('lib/orientation');

/**
* Exports
Expand All @@ -16,22 +15,33 @@ module.exports = addPanAndZoomHandlers;

/*
* This module adds pan-and-zoom capability to images displayed by
* shared/js/media/media_frame.js.
* shared/js/media/media_frame.js.
* It is used by preview-gallery.js and confirm.js
*/
function addPanAndZoomHandlers(frame) {
// frame is the MediaFrame object. container is its the DOM element.
function addPanAndZoomHandlers(frame, swipeCallback) {
// frame is the MediaFrame object. container is its DOM element.
var container = frame.container;

// Generate gesture events for the container
var gestureDetector = new GestureDetector(container);
gestureDetector.startDetecting();

// When the user touches the screen and moves their finger left or
// right, they might want to pan within a zoomed-in image, or they
// might want to swipe between multiple items in the camera preview
// gallery. We pass the amount of motion to the MediaFrame pan() method,
// and it returns the amount that cannot be used to pan the displayed
// item. We track this returned amount as how far left or right the
// image has been swiped, and pass the number to the swipeCallback.
var swipeAmount = 0;

// And handle them with these listeners
container.addEventListener('dbltap', handleDoubleTap);
container.addEventListener('transform', handleTransform);
container.addEventListener('pan', handlePan);
container.addEventListener('swipe', handleSwipe);
if (swipeCallback) {
container.addEventListener('swipe', handleSwipe);
}

function handleDoubleTap(e) {
var scale;
Expand All @@ -42,104 +52,32 @@ function addPanAndZoomHandlers(frame) {
scale = 2;
}

// If the phone orientation is 0 (unrotated) then the gesture detector's
// event coordinates match what's on the screen, and we use them to
// specify a point to zoom in or out on. For other orientations we could
// calculate the correct point, but instead just use the midpoint.
var x, y;
if (orientation.get() === 0) {
x = e.detail.clientX;
y = e.detail.clientY;
}
else {
x = container.offsetWidth / 2;
y = container.offsetHeight / 2;
}

frame.zoom(scale, x, y, 200);
frame.zoom(scale, e.detail.clientX, e.detail.clientY, 200);
}

function handleTransform(e) {
// If the phone orientation is 0 (unrotated) then the gesture detector's
// event coordinates match what's on the screen, and we use them to
// specify a point to zoom in or out on. For other orientations we could
// calculate the correct point, but instead just use the midpoint.
var x, y;
if (orientation.get() === 0) {
x = e.detail.midpoint.clientX;
y = e.detail.midpoint.clientY;
}
else {
x = container.offsetWidth / 2;
y = container.offsetHeight / 2;
}

frame.zoom(e.detail.relative.scale, x, y);
frame.zoom(e.detail.relative.scale,
e.detail.midpoint.clientX, e.detail.midpoint.clientY);
}

function handlePan(e) {
// The gesture detector event does not take our CSS rotation into
// account, so we have to pan by a dx and dy that depend on how
// the MediaFrame is rotated
var dx, dy;
switch (orientation.get()) {
case 0:
dx = e.detail.relative.dx;
dy = e.detail.relative.dy;
break;
case 90:
dx = -e.detail.relative.dy;
dy = e.detail.relative.dx;
break;
case 180:
dx = -e.detail.relative.dx;
dy = -e.detail.relative.dy;
break;
case 270:
dx = e.detail.relative.dy;
dy = -e.detail.relative.dx;
break;
var dx = e.detail.relative.dx;
var dy = e.detail.relative.dy;

if (swipeCallback) {
dx += swipeAmount;
swipeAmount = frame.pan(dx, dy);
swipeCallback(swipeAmount);
} else {
frame.pan(dx, dy);
}

frame.pan(dx, dy);
}

function handleSwipe(e) {
var direction = e.detail.direction;
switch (orientation.get()) {
case 90:
switch (e.detail.direction) {
case 'up': direction = 'right'; break;
case 'down': direction = 'left'; break;
case 'left': direction = 'up'; break;
case 'right': direction = 'down'; break;
}
break;
case 180:
switch (e.detail.direction) {
case 'up': direction = 'down'; break;
case 'down': direction = 'up'; break;
case 'left': direction = 'right'; break;
case 'right': direction = 'left'; break;
}
break;
case 270:
switch (e.detail.direction) {
case 'up': direction = 'left'; break;
case 'down': direction = 'right'; break;
case 'left': direction = 'down'; break;
case 'right': direction = 'up'; break;
}
break;
if (swipeAmount !== 0) {
swipeCallback(swipeAmount, e.detail.vx);
swipeAmount = 0;
}
e.detail.direction = direction;

var itemChangeEvent = new CustomEvent('orientationSwipe', {
detail: e.detail
});
/*jshint validthis:true */
this.dispatchEvent(itemChangeEvent);

}
}

Expand Down
27 changes: 21 additions & 6 deletions apps/camera/js/vendor/orientation.js
Expand Up @@ -23,7 +23,7 @@ define(function() {

var lastMotionFilteredTime = 0;
var lastMotionData = {x: 0, y: 0, z: 0, t: 0};
var pendingOrientation = 0;
var pendingOrientation = null;
var orientationChangeTimer = 0;
var eventListeners = {'orientation': []};

Expand Down Expand Up @@ -123,20 +123,35 @@ define(function() {
window.clearTimeout(orientationChangeTimer);
}

// create timer for waiting to rotate the phone
pendingOrientation = orientation;
orientationChangeTimer = window.setTimeout(function doOrient() {
// If we don't have any current orientation, then send an event right away
// Otherwise, wait to make sure we're stable before sending it.
if (pendingOrientation === null) {
pendingOrientation = orientation;
fireOrientationChangeEvent(pendingOrientation);
orientationChangeTimer = 0;
}, ORIENTATION_CHANGE_INTERVAL);
}
else {
// create timer for waiting to rotate the phone
pendingOrientation = orientation;
orientationChangeTimer = window.setTimeout(function doOrient() {
fireOrientationChangeEvent(pendingOrientation);
orientationChangeTimer = 0;
}, ORIENTATION_CHANGE_INTERVAL);
}
}

function start() {
// Reset our state so that the first devicemotion event we get
// will always generate an orientation event.
pendingOrientation = null;
window.addEventListener('devicemotion', handleMotionEvent);
}

function stop() {
window.removeEventListener('devicemotion', handleMotionEvent);
if (orientationChangeTimer) {
clearTimeout(orientationChangeTimer);
orientationChangeTimer = 0;
}
}

function addEventListener(type, listener) {
Expand Down
12 changes: 12 additions & 0 deletions apps/camera/js/views/confirm.js
Expand Up @@ -9,6 +9,7 @@ var addPanAndZoomHandlers = require('lib/panzoom');
var MediaFrame = require('MediaFrame');
var View = require('vendor/view');
var bind = require('lib/bind');
var orientation = require('lib/orientation');

/**
* Exports
Expand Down Expand Up @@ -47,15 +48,18 @@ module.exports = View.extend({
setupMediaFrame: function() {
this.mediaFrame = new MediaFrame(this.els.mediaFrame);
addPanAndZoomHandlers(this.mediaFrame);
window.addEventListener('resize', this.onResize);
return this;
},

hide: function() {
this.el.classList.add('hidden');
orientation.lock();
},

show: function() {
this.el.classList.remove('hidden');
orientation.unlock();
},

showImage: function(image) {
Expand Down Expand Up @@ -96,7 +100,15 @@ module.exports = View.extend({
this.emit('click:' + name);
},

onResize: function() {
this.mediaFrame.resize();
if (this.mediaFrame.displayingVideo) {
this.mediaFrame.video.setPlayerSize();
}
},

onDestroy: function() {
window.removeEventListener('resize', this.onResize);
this.mediaFrame.clear();
}
});
Expand Down

0 comments on commit f46815a

Please sign in to comment.