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 #18871 from justindarc/bug1000984-v1.4
Browse files Browse the repository at this point in the history
Bug 1000984 - [Camera] Pinch-to-zoom gesture should be attached to the |...
  • Loading branch information
justindarc committed May 2, 2014
2 parents 52672c4 + dee63fc commit 7b2b82d
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 88 deletions.
6 changes: 6 additions & 0 deletions apps/camera/js/app.js
Expand Up @@ -15,6 +15,7 @@ var bindAll = require('lib/bind-all');
var model = require('vendor/model');
var debug = require('debug')('app');
var HudView = require('views/hud');
var Pinch = require('lib/pinch');
var bind = require('lib/bind');

/**
Expand Down Expand Up @@ -152,6 +153,11 @@ App.prototype.bindEvents = function() {
bind(this.el, 'click', this.onClick);
this.on('focus', this.onFocus);
this.on('blur', this.onBlur);

// Pinch
this.pinch = new Pinch(this.el);
this.pinch.on('pinchchanged', this.firer('pinchchanged'));

debug('events bound');
};

Expand Down
9 changes: 7 additions & 2 deletions apps/camera/js/controllers/viewfinder.js
Expand Up @@ -7,6 +7,7 @@ define(function(require, exports, module) {

var debug = require('debug')('controller:viewfinder');
var bindAll = require('lib/bind-all');
var constants = require('config/camera');

/**
* Exports
Expand Down Expand Up @@ -40,6 +41,8 @@ function ViewfinderController(app) {
* @private
*/
ViewfinderController.prototype.configure = function() {
this.sensitivity = constants.ZOOM_GESTURE_SENSITIVITY * window.innerWidth;

this.configureScaleType();
this.configureGrid();
};
Expand Down Expand Up @@ -84,7 +87,6 @@ ViewfinderController.prototype.hideGrid = function() {
ViewfinderController.prototype.bindEvents = function() {
this.app.settings.grid.on('change:selected', this.viewfinder.setter('grid'));
this.viewfinder.on('click', this.app.firer('viewfinder:click'));
this.viewfinder.on('pinchChange', this.onPinchChange);
this.camera.on('zoomchanged', this.onZoomChanged);
this.app.on('camera:focuschanged', this.focusRing.setState);
this.app.on('camera:configured', this.onCameraConfigured);
Expand All @@ -94,6 +96,7 @@ ViewfinderController.prototype.bindEvents = function() {
this.app.on('settings:closed', this.configureGrid);
this.app.on('settings:opened', this.hideGrid);
this.app.on('blur', this.stopStream);
this.app.on('pinchchanged', this.onPinchChanged);
};

/**
Expand Down Expand Up @@ -200,7 +203,9 @@ ViewfinderController.prototype.configureZoom = function() {
*
* @private
*/
ViewfinderController.prototype.onPinchChange = function(zoom) {
ViewfinderController.prototype.onPinchChanged = function(deltaPinch) {
var zoom = this.viewfinder._zoom * (1 + (deltaPinch / this.sensitivity));
this.viewfinder.setZoom(zoom);
this.camera.setZoom(zoom);
};

Expand Down
8 changes: 4 additions & 4 deletions apps/camera/js/controllers/zoom-bar.js
Expand Up @@ -34,8 +34,8 @@ ZoomBarController.prototype.bindEvents = function() {
this.zoomBar.on('change', this.onChange);
this.camera.on('zoomconfigured', this.onZoomConfigured);
this.camera.on('zoomchanged', this.onZoomChanged);
this.viewfinder.on('pinchStart', this.onPinchStart);
this.viewfinder.on('pinchEnd', this.onPinchEnd);
this.viewfinder.on('pinchstarted', this.onPinchStarted);
this.viewfinder.on('pinchended', this.onPinchEnded);
};

ZoomBarController.prototype.onChange = function(value) {
Expand All @@ -58,11 +58,11 @@ ZoomBarController.prototype.onZoomChanged = function(zoom) {
this.zoomBar.setValue(percent);
};

ZoomBarController.prototype.onPinchStart = function() {
ZoomBarController.prototype.onPinchStarted = function() {
this.zoomBar.setScrubberActive(true);
};

ZoomBarController.prototype.onPinchEnd = function() {
ZoomBarController.prototype.onPinchEnded = function() {
this.zoomBar.setScrubberActive(false);
};

Expand Down
132 changes: 132 additions & 0 deletions apps/camera/js/lib/pinch.js
@@ -0,0 +1,132 @@
define(function(require, exports, module) {
'use strict';

/**
* Dependencies
*/

var bind = require('lib/bind');
var unbind = require('lib/bind').unbind;
var bindAll = require('lib/bind-all');
var events = require('vendor/evt');

/**
* Mixin event emitter
*/

events(Pinch.prototype);

/**
* Exports
*/

module.exports = Pinch;

/**
* Initialize a new `Pinch` interface.
*
* @constructor
*/
function Pinch(el) {
bindAll(this);
this.attach(el);
}

Pinch.prototype.attach = function(el) {
this.el = el;

bind(this.el, 'touchstart', this.onTouchStart);
bind(window, 'touchmove', this.onTouchMove);
bind(window, 'touchend', this.onTouchEnd);
};

Pinch.prototype.detach = function() {
unbind(this.el, 'touchstart', this.onTouchStart);
unbind(window, 'touchmove', this.onTouchMove);
unbind(window, 'touchend', this.onTouchEnd);

this.el = null;
};

Pinch.prototype.onTouchStart = function(evt) {
if (evt.touches.length !== 2) {
return;
}

this.lastTouchA = evt.touches[0];
this.lastTouchB = evt.touches[1];
this.isPinching = true;
this.emit('pinchstarted');
};

Pinch.prototype.onTouchMove = function(evt) {
if (!this.isPinching) {
return;
}

var touchA = getNewTouchA(this, evt.touches);
var touchB = getNewTouchB(this, evt.touches);
var deltaPinch = getDeltaPinch(this, touchA, touchB);

this.emit('pinchchanged', deltaPinch);

this.lastTouchA = touchA;
this.lastTouchB = touchB;
};

Pinch.prototype.onTouchEnd = function(evt) {
if (!this.isPinching) {
return;
}

if (evt.touches.length < 2) {
this.isPinching = false;
this.emit('pinchended');
}
};

function getNewTouchA(pinch, touches) {
if (!pinch.lastTouchA) {
return null;
}

for (var i = 0, length = touches.length, touch; i < length; i++) {
touch = touches[i];
if (touch.identifier === pinch.lastTouchA.identifier) {
return touch;
}
}
return null;
}

function getNewTouchB(pinch, touches) {
if (!pinch.lastTouchB) {
return null;
}

for (var i = 0, length = touches.length, touch; i < length; i++) {
touch = touches[i];
if (touch.identifier === pinch.lastTouchB.identifier) {
return touch;
}
}
return null;
}

function getDeltaPinch(pinch, touchA, touchB) {
var lastTouchA = pinch.lastTouchA;
var lastTouchB = pinch.lastTouchB;
if (!touchA || !lastTouchA || !touchB || !lastTouchB) {
return 0;
}

var oldDistance = Math.sqrt(
Math.pow(lastTouchB.pageX - lastTouchA.pageX, 2) +
Math.pow(lastTouchB.pageY - lastTouchA.pageY, 2));
var newDistance = Math.sqrt(
Math.pow(touchB.pageX - touchA.pageX, 2) +
Math.pow(touchB.pageY - touchA.pageY, 2));
return newDistance - oldDistance;
}

});
82 changes: 0 additions & 82 deletions apps/camera/js/views/viewfinder.js
Expand Up @@ -8,53 +8,18 @@ define(function(require, exports, module) {
var bind = require('lib/bind');
var CameraUtils = require('lib/camera-utils');
var debug = require('debug')('view:viewfinder');
var constants = require('config/camera');
var View = require('vendor/view');

/**
* Locals
*/

var lastTouchA = null;
var lastTouchB = null;
var isScaling = false;
var isZoomEnabled = false;
var sensitivity;
var scaleSizeTo = {
fill: CameraUtils.scaleSizeToFillViewport,
fit: CameraUtils.scaleSizeToFitViewport
};

var getNewTouchA = function(touches) {
if (!lastTouchA) return null;
for (var i = 0, length = touches.length, touch; i < length; i++) {
touch = touches[i];
if (touch.identifier === lastTouchA.identifier) return touch;
}
return null;
};

var getNewTouchB = function(touches) {
if (!lastTouchB) return null;
for (var i = 0, length = touches.length, touch; i < length; i++) {
touch = touches[i];
if (touch.identifier === lastTouchB.identifier) return touch;
}
return null;
};

var getDeltaZoom = function(touchA, touchB) {
if (!touchA || !lastTouchA || !touchB || !lastTouchB) return 0;

var oldDistance = Math.sqrt(
Math.pow(lastTouchB.pageX - lastTouchA.pageX, 2) +
Math.pow(lastTouchB.pageY - lastTouchA.pageY, 2));
var newDistance = Math.sqrt(
Math.pow(touchB.pageX - touchA.pageX, 2) +
Math.pow(touchB.pageY - touchA.pageY, 2));
return newDistance - oldDistance;
};

var clamp = function(value, minimum, maximum) {
return Math.min(Math.max(value, minimum), maximum);
};
Expand All @@ -68,9 +33,6 @@ module.exports = View.extend({
this.render();

bind(this.el, 'click', this.onClick);
bind(this.el, 'touchstart', this.onTouchStart);
bind(this.el, 'touchmove', this.onTouchMove);
bind(this.el, 'touchend', this.onTouchEnd);
bind(this.el, 'animationend', this.onShutterEnd);
},

Expand All @@ -81,56 +43,12 @@ module.exports = View.extend({
this.els.frame = this.find('.js-frame');
this.els.video = this.find('.js-video');
this.els.videoContainer = this.find('.js-video-container');

sensitivity = constants.ZOOM_GESTURE_SENSITIVITY * window.innerWidth;
},

onClick: function(e) {
this.emit('click');
},

onTouchStart: function(evt) {
var touchCount = evt.targetTouches.length;
if (touchCount === 2) {
lastTouchA = evt.targetTouches[0];
lastTouchB = evt.targetTouches[1];
isScaling = true;
this.emit('pinchStart');

evt.preventDefault();
}
},

onTouchMove: function(evt) {
if (!isScaling) {
return;
}

var touchA = getNewTouchA(evt.targetTouches);
var touchB = getNewTouchB(evt.targetTouches);

var deltaZoom = getDeltaZoom(touchA, touchB);
var zoom = this._zoom * (1 + (deltaZoom / sensitivity));

this.setZoom(zoom);

this.emit('pinchChange', this._zoom);

lastTouchA = touchA;
lastTouchB = touchB;
},

onTouchEnd: function(evt) {
if (!isScaling) {
return;
}

if (evt.targetTouches.length < 2) {
isScaling = false;
this.emit('pinchEnd');
}
},

enableZoom: function(minimumZoom, maximumZoom) {
if (minimumZoom) {
this._minimumZoom = minimumZoom;
Expand Down

0 comments on commit 7b2b82d

Please sign in to comment.