Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Merge pull request #18059 from davidflanagan/camera-preview
Browse files Browse the repository at this point in the history
Bug 989361: better swipe handling for switching items in preview r=justindarc
  • Loading branch information
David Flanagan committed Apr 10, 2014
2 parents 3f07c15 + f46815a commit fb29a8e
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 fb29a8e

Please sign in to comment.