Index: components/dom_distiller/core/javascript/dom_distiller_viewer.js |
diff --git a/components/dom_distiller/core/javascript/dom_distiller_viewer.js b/components/dom_distiller/core/javascript/dom_distiller_viewer.js |
index 55f53bbf3854feaebdf305bed92df2acce7a9ef2..551a8cbeee1748c2e62df347e62062e9069514af 100644 |
--- a/components/dom_distiller/core/javascript/dom_distiller_viewer.js |
+++ b/components/dom_distiller/core/javascript/dom_distiller_viewer.js |
@@ -74,3 +74,123 @@ document.getElementById('showOriginal').addEventListener('click', function(e) { |
document.body.appendChild(img); |
}, true); |
+window.addEventListener('load', function() { |
+ // When users pinch in Reader Mode, the page would zoom in or out as if it |
+ // is a normal web page allowing user-zoom. At the end of pinch gesture, the |
+ // page would do text reflow. These pinch-to-zoom and text reflow effects |
+ // are not native, but are emulated using CSS and JavaScript. |
+ // |
+ // In order to achieve near-native zooming and panning frame rate, fake 3D |
+ // transform is used so that the layer doesn't repaint for each frame. |
+ // |
+ // After the text reflow, the web content shown in viewport should roughly be |
+ // the same paragraph before zooming. |
+ // |
+ // The control point of font size is the html element, so that both "em" and |
+ // "rem" are adjusted. |
+ // |
+ // TODO(wychen): Improve scroll position when elementFromPoint is body. |
+ |
+ var pinching = false; |
+ var pinchDistAnchor = 0; |
+ var fontSizeAnchor = 1.0; |
+ var focusElement = null; |
+ var focusPos = 0; |
+ var pinchOrigin = {x: 0, y: 0}; |
+ var scaleFactor = 1; |
+ var lastClientMidY = 0; |
+ |
+ // The zooming speed relative to pinching speed. |
+ // @const |
+ var FONT_SCALE_MULTIPLIER = 0.3; |
+ |
+ // The font size is guaranteed to be in px. |
+ var baseSize = |
+ parseFloat(getComputedStyle(document.documentElement).fontSize); |
+ |
+ var handleTouchStart = function(e) { |
+ if (e.touches.length < 2) return; |
+ e.preventDefault(); |
+ if (e.touches.length != 2) return; |
jdduke (slow)
2015/03/16 20:27:52
How much more complicated is supporting >2 touches
wychen
2015/03/17 17:36:11
Good suggestion! I've never used more than two fin
|
+ |
+ pinchDistAnchor = touchDist(e); |
+ pinching = pinchDistAnchor > 0; |
+ fontSizeAnchor = |
+ parseFloat(getComputedStyle(document.documentElement).fontSize) |
+ / baseSize; |
+ |
+ pinchOrigin = touchPageMid(e); |
+ var clientMid = touchClientMid(e); |
+ var pinchMidY = clientMid.y; |
+ // Try to preserve the pinching center after text reflow. |
+ // This is accurate to the HTML element level. |
+ focusElement = document.elementFromPoint(clientMid.x, clientMid.y); |
+ var rect = focusElement.getBoundingClientRect(); |
+ focusPos = (pinchMidY - rect.top) / (rect.bottom - rect.top); |
+ |
+ lastClientMidY = clientMid.y; |
+ }; |
+ |
+ var handleTouchMove = function(e) { |
+ if (!pinching) return; |
+ e.preventDefault(); |
+ |
+ var pinchScale = touchDist(e) / pinchDistAnchor; |
+ var fontScale = 1 + ((pinchScale - 1) * FONT_SCALE_MULTIPLIER); |
jdduke (slow)
2015/03/16 20:27:52
We might want to be more careful about the min fon
wychen
2015/03/17 17:36:11
0.4 might be in the unreadable range, and 0.4~2.5
|
+ scaleFactor = Math.max(0.4, Math.min(2.5, fontSizeAnchor * fontScale)); |
+ var mid = touchPageMid(e); |
+ |
+ document.body.style.transformOrigin = |
+ pinchOrigin.x + 'px ' + pinchOrigin.y + 'px'; |
+ // Use "fake" 3D transform so that the layer is not repainted. |
+ // With 2D transform, the frame rate would be much lower. |
jdduke (slow)
2015/03/16 20:27:53
I can't speak to this logic.
wychen
2015/03/17 17:36:12
The frame rate was enhanced from <30fps to 60fps t
|
+ document.body.style.transform = |
+ 'translate3d(' + (mid.x - pinchOrigin.x) + 'px,' + |
+ (mid.y - pinchOrigin.y) + 'px, 0px)' + |
+ 'scale(' + scaleFactor/fontSizeAnchor + ')'; |
+ |
+ lastClientMidY = touchClientMid(e).y; |
+ }; |
+ |
+ var handleTouchEnd = function(e) { |
+ if (!pinching) return; |
jdduke (slow)
2015/03/16 20:27:53
We'll need to be careful here as well, as we proba
wychen
2015/03/17 17:36:11
Indeed.
|
+ e.preventDefault(); |
+ pinching = false; |
+ |
+ document.body.style.transformOrigin = ''; |
+ document.body.style.transform = ''; |
+ document.documentElement.style.fontSize = scaleFactor * baseSize + "px"; |
jdduke (slow)
2015/03/16 20:27:52
I'm an HTML newb so we'll want somebody else to re
wychen
2015/03/17 17:36:11
Why would this line in particular concern you?
Th
jdduke (slow)
2015/03/17 17:49:51
Oh it's not that particular line that concerns me,
|
+ |
+ var rect = focusElement.getBoundingClientRect(); |
+ var targetTop = focusPos * (rect.bottom - rect.top) + rect.top + |
+ document.body.scrollTop - lastClientMidY; |
+ document.body.scrollTop = targetTop; |
+ }; |
+ |
+ var handleTouchCancel = function(e) { |
+ pinching = false; |
+ }; |
+ |
+ function touchDist(e) { |
+ var dx = (e.touches[0].screenX-e.touches[1].screenX); |
+ var dy = (e.touches[0].screenY-e.touches[1].screenY); |
+ return Math.sqrt(dx * dx + dy * dy); |
+ } |
+ |
+ function touchClientMid(e) { |
+ var mx = (e.touches[0].clientX+e.touches[1].clientX)/2; |
+ var my = (e.touches[0].clientY+e.touches[1].clientY)/2; |
+ return {x: mx, y: my}; |
+ } |
+ |
+ function touchPageMid(e) { |
+ var mx = (e.touches[0].pageX+e.touches[1].pageX)/2; |
+ var my = (e.touches[0].pageY+e.touches[1].pageY)/2; |
+ return {x: mx, y: my}; |
+ } |
+ |
+ window.addEventListener('touchstart', handleTouchStart, false); |
+ window.addEventListener('touchmove', handleTouchMove, false); |
+ window.addEventListener('touchend', handleTouchEnd, false); |
+ window.addEventListener('touchcancel', handleTouchCancel, false); |
+}); |