Chromium Code Reviews| 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); |
| +}); |