Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(977)

Side by Side Diff: chrome/browser/resources/pdf/viewport.js

Issue 2400743002: Improved Pinch-Zoom for PDF. (Closed)
Patch Set: JS code review changes. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * Returns the height of the intersection of two rectangles. 6 * Returns the height of the intersection of two rectangles.
7 * @param {Object} rect1 the first rect 7 * @param {Object} rect1 the first rect
8 * @param {Object} rect2 the second rect 8 * @param {Object} rect2 the second rect
9 * @return {number} the height of the intersection of the rects 9 * @return {number} the height of the intersection of the rects
10 */ 10 */
11 function getIntersectionHeight(rect1, rect2) { 11 function getIntersectionHeight(rect1, rect2) {
12 return Math.max(0, 12 return Math.max(0,
13 Math.min(rect1.y + rect1.height, rect2.y + rect2.height) - 13 Math.min(rect1.y + rect1.height, rect2.y + rect2.height) -
14 Math.max(rect1.y, rect2.y)); 14 Math.max(rect1.y, rect2.y));
15 } 15 }
16 16
17 /** 17 /**
18 * Makes sure that the scale level doesn't get out of the limits.
19 * @param {number} scale The new scale level.
20 * @return {number} The scale clamped within the limits.
21 */
22 function clampScale(scale) {
23 return Math.min(5, Math.max(0.25, scale));
24 }
25
26 /**
27 * Computes vector between two points.
28 * @param {!Object} p1 The first point.
29 * @param {!Object} p2 The second point.
30 * @return {!Object} The vector.
31 */
32 function vectorDelta(p1, p2) {
33 return {
34 x: p2.x - p1.x,
35 y: p2.y - p1.y
36 };
37 }
38
39 function frameToPluginCoordinate(coordinateInFrame) {
40 var container = $('plugin');
41 return {
42 x: coordinateInFrame.x - container.getBoundingClientRect().left,
43 y: coordinateInFrame.y - container.getBoundingClientRect().top
44 };
45 }
46
47 /**
18 * Create a new viewport. 48 * Create a new viewport.
19 * @constructor 49 * @constructor
20 * @param {Window} window the window 50 * @param {Window} window the window
21 * @param {Object} sizer is the element which represents the size of the 51 * @param {Object} sizer is the element which represents the size of the
22 * document in the viewport 52 * document in the viewport
23 * @param {Function} viewportChangedCallback is run when the viewport changes 53 * @param {Function} viewportChangedCallback is run when the viewport changes
24 * @param {Function} beforeZoomCallback is run before a change in zoom 54 * @param {Function} beforeZoomCallback is run before a change in zoom
25 * @param {Function} afterZoomCallback is run after a change in zoom 55 * @param {Function} afterZoomCallback is run after a change in zoom
26 * @param {number} scrollbarWidth the width of scrollbars on the page 56 * @param {number} scrollbarWidth the width of scrollbars on the page
27 * @param {number} defaultZoom The default zoom level. 57 * @param {number} defaultZoom The default zoom level.
(...skipping 14 matching lines...) Expand all
42 this.beforeZoomCallback_ = beforeZoomCallback; 72 this.beforeZoomCallback_ = beforeZoomCallback;
43 this.afterZoomCallback_ = afterZoomCallback; 73 this.afterZoomCallback_ = afterZoomCallback;
44 this.allowedToChangeZoom_ = false; 74 this.allowedToChangeZoom_ = false;
45 this.zoom_ = 1; 75 this.zoom_ = 1;
46 this.documentDimensions_ = null; 76 this.documentDimensions_ = null;
47 this.pageDimensions_ = []; 77 this.pageDimensions_ = [];
48 this.scrollbarWidth_ = scrollbarWidth; 78 this.scrollbarWidth_ = scrollbarWidth;
49 this.fittingType_ = Viewport.FittingType.NONE; 79 this.fittingType_ = Viewport.FittingType.NONE;
50 this.defaultZoom_ = defaultZoom; 80 this.defaultZoom_ = defaultZoom;
51 this.topToolbarHeight_ = topToolbarHeight; 81 this.topToolbarHeight_ = topToolbarHeight;
82 this.prevScale_ = 1;
83 this.pinchPhase_ = Viewport.PinchPhase.PINCH_NONE;
84 this.pinchPanVector_ = null;
85 this.pinchCenter_ = null;
86 this.firstPinchCenterInFrame_ = null;
52 87
53 window.addEventListener('scroll', this.updateViewport_.bind(this)); 88 window.addEventListener('scroll', this.updateViewport_.bind(this));
54 window.addEventListener('resize', this.resize_.bind(this)); 89 window.addEventListener('resize', this.resize_.bind(this));
55 } 90 }
56 91
57 /** 92 /**
58 * Enumeration of page fitting types. 93 * Enumeration of page fitting types.
59 * @enum {string} 94 * @enum {string}
60 */ 95 */
61 Viewport.FittingType = { 96 Viewport.FittingType = {
62 NONE: 'none', 97 NONE: 'none',
63 FIT_TO_PAGE: 'fit-to-page', 98 FIT_TO_PAGE: 'fit-to-page',
64 FIT_TO_WIDTH: 'fit-to-width' 99 FIT_TO_WIDTH: 'fit-to-width'
65 }; 100 };
66 101
67 /** 102 /**
103 * Enumeration of pinch states.
104 * This should match PinchPhase enum in pdf/out_of_process_instance.h
105 * @enum {number}
106 */
107 Viewport.PinchPhase = {
108 PINCH_NONE: 0,
109 PINCH_START: 1,
110 PINCH_UPDATE_ZOOM_OUT: 2,
111 PINCH_UPDATE_ZOOM_IN: 3,
112 PINCH_END: 4
113 };
114
115 /**
68 * The increment to scroll a page by in pixels when up/down/left/right arrow 116 * The increment to scroll a page by in pixels when up/down/left/right arrow
69 * keys are pressed. Usually we just let the browser handle scrolling on the 117 * keys are pressed. Usually we just let the browser handle scrolling on the
70 * window when these keys are pressed but in certain cases we need to simulate 118 * window when these keys are pressed but in certain cases we need to simulate
71 * these events. 119 * these events.
72 */ 120 */
73 Viewport.SCROLL_INCREMENT = 40; 121 Viewport.SCROLL_INCREMENT = 40;
74 122
75 /** 123 /**
76 * Predefined zoom factors to be used when zooming in/out. These are in 124 * Predefined zoom factors to be used when zooming in/out. These are in
77 * ascending order. This should match the lists in 125 * ascending order. This should match the lists in
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 }, 268 },
221 269
222 /** 270 /**
223 * @type {number} the zoom level of the viewport. 271 * @type {number} the zoom level of the viewport.
224 */ 272 */
225 get zoom() { 273 get zoom() {
226 return this.zoom_; 274 return this.zoom_;
227 }, 275 },
228 276
229 /** 277 /**
278 * @type {Viewport.PinchPhase} The phase of the current pinch gesture for
279 * the viewport.
280 */
281 get pinchPhase() {
282 return this.pinchPhase_;
283 },
284
285 /**
286 * @type {Object} The panning caused by the current pinch gesture (as
287 * the deltas of the x and y coordinates).
288 */
289 get pinchPanVector() {
290 return this.pinchPanVector_;
291 },
292
293 /**
294 * @type {Object} The coordinates of the center of the current pinch gesture.
295 */
296 get pinchCenter() {
297 return this.pinchCenter_;
298 },
299
300 /**
230 * @private 301 * @private
231 * Used to wrap a function that might perform zooming on the viewport. This is 302 * Used to wrap a function that might perform zooming on the viewport. This is
232 * required so that we can notify the plugin that zooming is in progress 303 * required so that we can notify the plugin that zooming is in progress
233 * so that while zooming is taking place it can stop reacting to scroll events 304 * so that while zooming is taking place it can stop reacting to scroll events
234 * from the viewport. This is to avoid flickering. 305 * from the viewport. This is to avoid flickering.
235 */ 306 */
236 mightZoom_: function(f) { 307 mightZoom_: function(f) {
237 this.beforeZoomCallback_(); 308 this.beforeZoomCallback_();
238 this.allowedToChangeZoom_ = true; 309 this.allowedToChangeZoom_ = true;
239 f(); 310 f();
(...skipping 19 matching lines...) Expand all
259 this.zoom_ = newZoom; 330 this.zoom_ = newZoom;
260 this.contentSizeChanged_(); 331 this.contentSizeChanged_();
261 // Scroll to the scaled scroll position. 332 // Scroll to the scaled scroll position.
262 this.position = { 333 this.position = {
263 x: currentScrollPos.x * newZoom, 334 x: currentScrollPos.x * newZoom,
264 y: currentScrollPos.y * newZoom 335 y: currentScrollPos.y * newZoom
265 }; 336 };
266 }, 337 },
267 338
268 /** 339 /**
340 * @private
341 * Sets the zoom of the viewport.
342 * Same as setZoomInternal_ but for pinch zoom we have some more operations.
343 * @param {number} scaleDelta The zoom delta.
344 * @param {!Object} center The pinch center in content coordinates.
345 */
346 setPinchZoomInternal_: function(scaleDelta, center) {
347 assert(this.allowedToChangeZoom_,
348 'Called Viewport.setPinchZoomInternal_ without calling ' +
349 'Viewport.mightZoom_.');
350 this.zoom_ = clampScale(this.zoom_ * scaleDelta);
351
352 var newCenterInContent = this.frameToContent(center);
353 var delta = {
354 x: (newCenterInContent.x - this.oldCenterInContent.x),
355 y: (newCenterInContent.y - this.oldCenterInContent.y)
356 };
357
358 // Record the scroll position (relative to the pinch center).
359 var currentScrollPos = {
360 x: this.position.x - delta.x * this.zoom_,
361 y: this.position.y - delta.y * this.zoom_
362 };
363
364 this.contentSizeChanged_();
365 // Scroll to the scaled scroll position.
366 this.position = {
367 x: currentScrollPos.x,
368 y: currentScrollPos.y
369 };
370 },
371
372 /**
373 * @private
374 * Converts a point from frame to content coordinates.
375 * @param {!Object} framePoint The frame coordinates.
376 * @return {!Object} The content coordinates.
377 */
378 frameToContent: function(framePoint) {
379 return {
dpapad 2016/11/09 01:12:27 For a future CL cleanup, I think this entire class
Kevin McNee - google account 2016/11/09 16:36:09 I've added a TODO about this. Acknowledged.
380 x: (framePoint.x + this.position.x) / this.zoom_,
381 y: (framePoint.y + this.position.y) / this.zoom_
382 };
383 },
384
385 /**
269 * Sets the zoom to the given zoom level. 386 * Sets the zoom to the given zoom level.
270 * @param {number} newZoom the zoom level to zoom to. 387 * @param {number} newZoom the zoom level to zoom to.
271 */ 388 */
272 setZoom: function(newZoom) { 389 setZoom: function(newZoom) {
273 this.fittingType_ = Viewport.FittingType.NONE; 390 this.fittingType_ = Viewport.FittingType.NONE;
274 newZoom = Math.max(Viewport.ZOOM_FACTOR_RANGE.min, 391 newZoom = Math.max(Viewport.ZOOM_FACTOR_RANGE.min,
275 Math.min(newZoom, Viewport.ZOOM_FACTOR_RANGE.max)); 392 Math.min(newZoom, Viewport.ZOOM_FACTOR_RANGE.max));
276 this.mightZoom_(function() { 393 this.mightZoom_(function() {
277 this.setZoomInternal_(newZoom); 394 this.setZoomInternal_(newZoom);
278 this.updateViewport_(); 395 this.updateViewport_();
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
492 for (var i = Viewport.ZOOM_FACTORS.length - 1; i >= 0; i--) { 609 for (var i = Viewport.ZOOM_FACTORS.length - 1; i >= 0; i--) {
493 if (Viewport.ZOOM_FACTORS[i] > this.zoom_) 610 if (Viewport.ZOOM_FACTORS[i] > this.zoom_)
494 nextZoom = Viewport.ZOOM_FACTORS[i]; 611 nextZoom = Viewport.ZOOM_FACTORS[i];
495 } 612 }
496 this.setZoomInternal_(nextZoom); 613 this.setZoomInternal_(nextZoom);
497 this.updateViewport_(); 614 this.updateViewport_();
498 }.bind(this)); 615 }.bind(this));
499 }, 616 },
500 617
501 /** 618 /**
619 * Pinch zoom event handler.
620 * @param {!Object} e The pinch event.
621 */
622 pinchZoom: function(e) {
623 this.mightZoom_(function() {
624 this.pinchPhase_ = e.direction == 'out' ?
625 Viewport.PinchPhase.PINCH_UPDATE_ZOOM_OUT :
626 Viewport.PinchPhase.PINCH_UPDATE_ZOOM_IN;
627
628 var scaleDelta = e.startScaleRatio / this.prevScale_;
629 this.pinchPanVector_ =
630 vectorDelta(e.center, this.firstPinchCenterInFrame_);
631
632 var needsScrollbars = this.documentNeedsScrollbars_(
633 clampScale(this.zoom_ * scaleDelta));
634
635 this.pinchCenter_ = e.center;
636
637 // If there's no horizontal scrolling, keep the content centered so the
638 // user can't zoom in on the non-content area.
639 // TODO(mcnee) Investigate other ways of scaling when we don't have
640 // horizontal scrolling. We want to keep the document centered,
641 // but this causes a potentially awkward transition when we start
642 // using the gesture center.
643 if (!needsScrollbars.horizontal) {
644 this.pinchCenter_ = {
645 x: this.window_.innerWidth / 2,
646 y: this.window_.innerHeight / 2
647 };
648 } else if (this.keepContentCentered_) {
649 this.oldCenterInContent =
650 this.frameToContent(frameToPluginCoordinate(e.center));
651 this.keepContentCentered_ = false;
652 }
653
654 this.setPinchZoomInternal_(
655 scaleDelta, frameToPluginCoordinate(e.center));
656 this.updateViewport_();
657 this.prevScale_ = e.startScaleRatio;
658 }.bind(this));
659 },
660
661 pinchZoomStart: function(e) {
662 this.pinchPhase_ = Viewport.PinchPhase.PINCH_START;
663 this.prevScale_ = 1;
664 this.oldCenterInContent =
665 this.frameToContent(frameToPluginCoordinate(e.center));
666
667 var needsScrollbars = this.documentNeedsScrollbars_(this.zoom_);
668 this.keepContentCentered_ = !needsScrollbars.horizontal;
669 // We keep track of begining of the pinch.
670 // By doing so we will be able to compute the pan distance.
671 this.firstPinchCenterInFrame_ = e.center;
672 },
673
674 pinchZoomEnd: function(e) {
675 this.mightZoom_(function() {
676 this.pinchPhase_ = Viewport.PinchPhase.PINCH_END;
677 var scaleDelta = e.startScaleRatio / this.prevScale_;
678 this.pinchCenter_ = e.center;
679
680 this.setPinchZoomInternal_(
681 scaleDelta, frameToPluginCoordinate(e.center));
682 this.updateViewport_();
683 }.bind(this));
684
685 this.pinchPhase_ = Viewport.PinchPhase.PINCH_NONE;
686 this.pinchPanVector_ = null;
687 this.pinchCenter_ = null;
688 this.firstPinchCenterInFrame_ = null;
689 },
690
691 /**
502 * Go to the given page index. 692 * Go to the given page index.
503 * @param {number} page the index of the page to go to. zero-based. 693 * @param {number} page the index of the page to go to. zero-based.
504 */ 694 */
505 goToPage: function(page) { 695 goToPage: function(page) {
506 this.mightZoom_(function() { 696 this.mightZoom_(function() {
507 if (this.pageDimensions_.length === 0) 697 if (this.pageDimensions_.length === 0)
508 return; 698 return;
509 if (page < 0) 699 if (page < 0)
510 page = 0; 700 page = 0;
511 if (page >= this.pageDimensions_.length) 701 if (page >= this.pageDimensions_.length)
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
590 spaceOnLeft = Math.max(spaceOnLeft, 0); 780 spaceOnLeft = Math.max(spaceOnLeft, 0);
591 781
592 return { 782 return {
593 x: x * this.zoom_ + spaceOnLeft - this.window_.pageXOffset, 783 x: x * this.zoom_ + spaceOnLeft - this.window_.pageXOffset,
594 y: insetDimensions.y * this.zoom_ - this.window_.pageYOffset, 784 y: insetDimensions.y * this.zoom_ - this.window_.pageYOffset,
595 width: insetDimensions.width * this.zoom_, 785 width: insetDimensions.width * this.zoom_,
596 height: insetDimensions.height * this.zoom_ 786 height: insetDimensions.height * this.zoom_
597 }; 787 };
598 } 788 }
599 }; 789 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698