Skip to content

Commit

Permalink
scanning: Fix multi-page scrolling algorithm
Browse files Browse the repository at this point in the history
- This change updates the scrolling/page focus algorithm. The previous
  one had limitations that kept users from scrolling through all the
  images in certain use cases.

- The new algorithm divides the total scroll area of the scanned images
  into proportionate intervals based on each individual image's height.
  These intervals are used to determine which image should be focused on
  based on the position of the scroll bar.

video: https://drive.google.com/file/d/1AR0JvWxH2dznG6mLwK4BdsBxSv-Ofb9b/view?usp=sharing&resourcekey=0-hBgOBMXyox4Mhebltn42Nw

Bug: 1259258
Change-Id: I2eb5bea9c00a3322b0e06d0e605c86ebcb18d6a7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3218432
Commit-Queue: Gavin Williams <gavinwill@chromium.org>
Reviewed-by: Zentaro Kavanagh <zentaro@chromium.org>
Cr-Commit-Position: refs/heads/main@{#930727}
  • Loading branch information
Gavin Williams authored and Chromium LUCI CQ committed Oct 12, 2021
1 parent a493b8f commit dcaed10
Showing 1 changed file with 60 additions and 17 deletions.
77 changes: 60 additions & 17 deletions ash/webui/scanning/resources/scan_preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ Polymer({
/** @private {number} */
actionToolbarWidth_: 0,

/**
* Stores the y value of the bottom of each scanned image in the preview div.
* The values are stored in the array in scanned image order so the value at
* [0] refers to the first scanned image, the value at [1] the second scanned
* image, etc.
* @private {!Array<number>}
*/
scrollingIntervals_: [],

properties: {
/** @type {!AppState} */
appState: {
Expand Down Expand Up @@ -338,7 +347,7 @@ Polymer({
}

// If the current page in view stays the same, do nothing.
const pageIndexInView = this.getCurrentPageInView_(scannedImages);
const pageIndexInView = this.getCurrentPageInView_();
if (pageIndexInView === this.currentPageIndexInView_) {
return;
}
Expand All @@ -347,28 +356,32 @@ Polymer({
},

/**
* Calculates the current page in view. Returns the page index of the highest
* page in the viewport unless that page is scrolled halfway outside the
* viewport, then it'll return the following page number. Assumes each scanned
* image is the same height.
* @param {!HTMLCollection} scannedImages
* Calculates the index of the current page in view based on where the current
* position of the scroll bar is found in |scrollingIntervals_|.
* @return {number}
* @private
*/
getCurrentPageInView_(scannedImages) {
getCurrentPageInView_() {
assert(this.isMultiPageScan);

const imageHeight = scannedImages[0].height;
const scrollTop = this.$$('#previewDiv').scrollTop - (imageHeight * .5);

// This is a special case for the first page since there is no margin or
// previous page above it.
if (scrollTop < 0) {
if (this.objectUrls.length === 1) {
return 0;
}

return 1 +
Math.floor(scrollTop / (imageHeight + SCANNED_IMG_MARGIN_BOTTOM_PX));
// |scrollTop| is a height value that ranges from 0 to the height at the
// bottom of div with all the scanned images. The height intervals in
// |scrollingIntervals_| are in order so the value at [0] refers to the
// first scanned image, at [1] the second scanned image, etc.
const scrollTop = this.$$('#previewDiv').scrollTop;
for (let i = 0; i < this.scrollingIntervals_.length; i++) {
// TODO(gavinwill): Convert from linear search to binary search.
if (scrollTop <= this.scrollingIntervals_[i]) {
return i;
}
}

// The last image is the catch-all interval.
return this.objectUrls.length - 1;
},

/**
Expand Down Expand Up @@ -425,8 +438,7 @@ Polymer({

const scannedImages =
this.$$('#scannedImages').getElementsByClassName('scanned-image');
this.setFocusedScannedImage_(
scannedImages, this.getCurrentPageInView_(scannedImages));
this.setFocusedScannedImage_(scannedImages, this.getCurrentPageInView_());

this.updatePreviewElements_();

Expand Down Expand Up @@ -638,6 +650,7 @@ Polymer({
* @private
*/
updatePreviewElements_() {
this.buildScrollingIntervals_();
this.setMultiPageScanProgressHeight_();
this.setActionToolbarPosition_();
},
Expand Down Expand Up @@ -676,4 +689,34 @@ Polymer({
return this.i18n(
'multiPageImageAriaLabel', index + 1, this.objectUrls.length);
},

/**
* Divides the total scroll area of the scanned images into proportionate
* intervals based on each individual image's height. These intervals are used
* to determine which image should be focused on based on the position of the
* scroll bar.
* @private
*/
buildScrollingIntervals_() {
const scannedImages =
this.$$('#scannedImages').getElementsByClassName('scanned-image');
if (scannedImages.length === 0) {
return;
}

const totalImagesHeight = this.$$('#previewDiv').scrollHeight;
const maxScrollTop =
totalImagesHeight - this.$$('#previewDiv').offsetHeight;
this.scrollingIntervals_ = [];
let currentIntervalHeight = 0;

// Only the first n - 1 images require intervals since the last image is the
// catch-all interval.
for (let i = 0; i < scannedImages.length - 1; i++) {
const scrollHeightProportion =
scannedImages[i].offsetHeight / totalImagesHeight;
currentIntervalHeight += maxScrollTop * scrollHeightProportion;
this.scrollingIntervals_.push(currentIntervalHeight);
}
},
});

0 comments on commit dcaed10

Please sign in to comment.