New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lightbox 2.0: add basic pinch zoom #12219
Lightbox 2.0: add basic pinch zoom #12219
Conversation
@@ -691,42 +693,44 @@ export class PinchRecognizer extends GestureRecognizer { | |||
/** @override */ | |||
onTouchStart(e) { | |||
const touches = e.touches; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should only stop tracking this series if touches are more than 2. If there has been one touch, PinchRecognizer
should wait for a second touch, since practically speaking, when you pinch a phone, the touches very rarely happen simultaneously.
} | ||
|
||
/** @override */ | ||
onTouchMove(e) { | ||
const touches = e.touches; | ||
if (!touches || touches.length != 2) { | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of returning false
, which causes PinchRecognizer
to stop tracking this event series, we return true
, but do not act on the touch event unless the number of touches is explicitly 2.
const dx2 = this.lastX2_ - this.startX2_; | ||
const dy2 = this.lastY2_ - this.startY2_; | ||
// Fingers should move in opposite directions and go over the threshold. | ||
if (dx1 * dx2 <= 0 && dy1 * dy2 <= 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The two boundaries here were tweaked from 8 to 3 and 10 respectively.
- Lowered the standards for registering a
signalReady
from 8 to 3. Empirically on the android tester, most pinch zooms here registered a delta of 3-8. Another possibility is to look at the total distance and to see if it is larger than 8. - Increased the standard for dropping this event series from 8 to 10, since swipes are usually a lot more generous than pinches.
This will be finalized after iOS testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empirical values should be sufficient, especially when distinguishing between two gestures. Let's move these thresholds from magic literals into named magic constants at the module level, like:
/**
* Threshold in pixels for ...
*/
const PINCH_THRESHOLD = 4;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -339,17 +340,25 @@ export class ImageViewer { | |||
const deltaX = this.viewerBox_.width / 2 - e.data.clientX; | |||
const deltaY = this.viewerBox_.height / 2 - e.data.clientY; | |||
this.onZoom_(newScale, deltaX, deltaY, true).then(() => { | |||
return this.onZoomRelease_(0, 0, 0, 0, 0, 0); | |||
return this.onZoomRelease_(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored this, since DoubleTap
zoom and Pinch
zoom only needs a subset of the onZoomRelease
functionality necessary for TapZoom
.
src/image-viewer.js
Outdated
* @param {number} deltaY | ||
* @param {number} dir | ||
* @private | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a wrapper for naming conventions. We could also take it out and use onZoomInc
. I'm not actually sure why it's called onZoomInc
. Maybe onZoomEvent
/ onZoomAction
would be more appropriate? Feedback welcome. =)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah onZoomInc
is probably not the best name. I generally prefer to avoid abbreviations in variable and function names for this reason. I guess it was onZoomIncrement
?
Maybe a better name could describe the action that the function accomplishes. zoomToPoint
? zoomInstant
? Naming is hard : P
* released. | ||
* @return {!Promise} | ||
* @private | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically refactored out this shared portion so that DoubleTap
zoom and Pinch
zoom can use this without unnecessary variables.
b2b37fc
to
2079d7e
Compare
2079d7e
to
6e2c52e
Compare
/to @aghassemi @cvializ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks awesome! I just have a few thoughts and questions
src/gesture-recognizers.js
Outdated
const touches = e.changedTouches || e.touches; | ||
if (touches && touches.length == 1) { | ||
const touches = e.touches; | ||
if (!touches || touches.length != 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this might be clearer with touches && touches.length == 1
and a return false;
at the end of the function. Thinking about what touches.length != 1
might mean takes a little more effort to reason about the condition.
src/gesture-recognizers.js
Outdated
const touches = e.changedTouches || e.touches; | ||
if (touches && touches.length == 1) { | ||
const touches = e.touches; | ||
if (!touches || touches.length != 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto the above
@@ -271,8 +273,10 @@ class SwipeRecognizer extends GestureRecognizer { | |||
|
|||
/** @override */ | |||
onTouchMove(e) { | |||
const touches = e.changedTouches || e.touches; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does changing this from e.changedTouches || e.touches;
to just touches = e.touches;
change the behavior? I notice the the TapzoomRecognizer#onTouchMove
still uses e.changedTouches
. Can you help me understand the different cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So changedTouches
refers to all touches involved in this event. Aka touches that moved, initiated, or were removed. touches
refers to all contact points with the screen. This changes the functionality of swipe so that it only works when one finger is on the screen--basically no matter how many changedTouches
there are, if more than one touch is on the screen, then we don't register a swipe. This was done because a large number of pinches were aggressively misread as swipes.
For TapZoom
, I'm not sure if we consider one finger on the screen (not moving), and a second finger moving a TapZoom
or a Pinch
. If we assign that to Pinch
and not TapZoom
, then we can make the same change to TapZoom
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, that makes sense. My initial impression is that one stationary finger and one moving finger should be a pinch zoom and not a tap zoom. macOS seems to require both fingers moving, but iOS's default behavior seems to allow one finger stationary or both fingers moving to pinch zoom
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. I've gone ahead and updated TapZoom
to const touches = e.touches
as well.
src/gesture-recognizers.js
Outdated
@@ -512,7 +516,9 @@ export class TapzoomRecognizer extends GestureRecognizer { | |||
/** @override */ | |||
onTouchMove(e) { | |||
const touches = e.changedTouches || e.touches; | |||
if (touches && touches.length == 1) { | |||
if (!touches || touches.length != 1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto the above
const dx2 = this.lastX2_ - this.startX2_; | ||
const dy2 = this.lastY2_ - this.startY2_; | ||
// Fingers should move in opposite directions and go over the threshold. | ||
if (dx1 * dx2 <= 0 && dy1 * dy2 <= 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empirical values should be sufficient, especially when distinguishing between two gestures. Let's move these thresholds from magic literals into named magic constants at the module level, like:
/**
* Threshold in pixels for ...
*/
const PINCH_THRESHOLD = 4;
src/image-viewer.js
Outdated
* @param {number} centerClientY | ||
* @param {number} deltaX | ||
* @param {number} deltaY | ||
* @param {number} dir |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the whitespace in this comment is incorrect. The stars should be at the same indentation as the first star in the jsdoc comment opener.
src/image-viewer.js
Outdated
* @param {number} deltaY | ||
* @param {number} dir | ||
* @private | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah onZoomInc
is probably not the best name. I generally prefer to avoid abbreviations in variable and function names for this reason. I guess it was onZoomIncrement
?
Maybe a better name could describe the action that the function accomplishes. zoomToPoint
? zoomInstant
? Naming is hard : P
const dx2 = this.lastX2_ - this.startX2_; | ||
const dy2 = this.lastY2_ - this.startY2_; | ||
// Fingers should move in opposite directions and go over the threshold. | ||
if (dx1 * dx2 <= 0 && dy1 * dy2 <= 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.data.deltaX, e.data.deltaY, e.data.velocityY, e.data.velocityY); | ||
} | ||
}); | ||
gestures.onGesture(PinchRecognizer, e => { | ||
// To disable iOS 10 Safari default pinch zoom | ||
event.preventDefault(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest removing disableTouchZoom
from enterOverlayMode
(a new PR might be better, maybe an issue to do it later).
Reasoning is:
1- the old disableTouchZoom
don't work on iOS anymore
2- Disabling touchzoom for every overlay is not the right thing to do. It only makes sense for image viewer and similar cases not every lightbox.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened #12254 as a separate PR.
src/image-viewer.js
Outdated
onTapZoom_(centerClientX, centerClientY, deltaX, deltaY) { | ||
const dir = Math.abs(deltaY) > Math.abs(deltaX) ? | ||
Math.sign(deltaY) : Math.sign(-deltaX); | ||
this.onZoomInc_(centerClientX, centerClientY, deltaX, deltaY, dir); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made me think of Monster Inc
:). Mayeb a different name? performZoom
, applyZoom
?
@cvializ @aghassemi PTAL at your leisure. =) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!!
* Add basic pinch zoom * Correct if / else chain in GestureRecognizers for clarity * Reindent comments and rename onZoomInc to zoomToPoint * Pull out threshold constants
* Add basic pinch zoom * Correct if / else chain in GestureRecognizers for clarity * Reindent comments and rename onZoomInc to zoomToPoint * Pull out threshold constants
This PR adds basic pinch zoom to
amp-lightbox-viewer
viaimage-viewer
, tested on Android/Chrome (iOS testing in progress).Some small tweaks were made to the behavior of
PinchRecognizer
andSwipeXYRecognizer
because the default implementation keeps causing pinches to be miscategorized as swipes. Comments inline.TODOs:
GestureRecognizers
have lovely and elaborate tests).Added a
event.preventDefault()
for iOS safari. The pinch zoom is now pretty smooth. =)