Use binary search in getVisibleElements()
This commit is contained in:
parent
a033b4bf3d
commit
a78bb6b946
37
test/unit/ui_utils_spec.js
Normal file
37
test/unit/ui_utils_spec.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
/* globals expect, it, describe, binarySearchFirstItem */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('ui_utils', function() {
|
||||||
|
|
||||||
|
describe('binary search', function() {
|
||||||
|
function isTrue(boolean) {
|
||||||
|
return boolean;
|
||||||
|
}
|
||||||
|
function isGreater3(number) {
|
||||||
|
return number > 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('empty array', function() {
|
||||||
|
expect(binarySearchFirstItem([], isTrue)).toEqual(0);
|
||||||
|
});
|
||||||
|
it('single boolean entry', function() {
|
||||||
|
expect(binarySearchFirstItem([false], isTrue)).toEqual(1);
|
||||||
|
expect(binarySearchFirstItem([true], isTrue)).toEqual(0);
|
||||||
|
});
|
||||||
|
it('three boolean entries', function() {
|
||||||
|
expect(binarySearchFirstItem([true, true, true], isTrue)).toEqual(0);
|
||||||
|
expect(binarySearchFirstItem([false, true, true], isTrue)).toEqual(1);
|
||||||
|
expect(binarySearchFirstItem([false, false, true], isTrue)).toEqual(2);
|
||||||
|
expect(binarySearchFirstItem([false, false, false], isTrue)).toEqual(3);
|
||||||
|
});
|
||||||
|
it('three numeric entries', function() {
|
||||||
|
expect(binarySearchFirstItem([0, 1, 2], isGreater3)).toEqual(3);
|
||||||
|
expect(binarySearchFirstItem([2, 3, 4], isGreater3)).toEqual(2);
|
||||||
|
expect(binarySearchFirstItem([4, 5, 6], isGreater3)).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -40,6 +40,7 @@
|
|||||||
<script src="../../src/core/worker.js"></script>
|
<script src="../../src/core/worker.js"></script>
|
||||||
<script src="../../src/display/metadata.js"></script>
|
<script src="../../src/display/metadata.js"></script>
|
||||||
<script src="../../src/core/jpg.js"></script>
|
<script src="../../src/core/jpg.js"></script>
|
||||||
|
<script src="../../web/ui_utils.js"></script>
|
||||||
<script>PDFJS.workerSrc = '../../src/worker_loader.js';</script>
|
<script>PDFJS.workerSrc = '../../src/worker_loader.js';</script>
|
||||||
|
|
||||||
<!-- include spec files here... -->
|
<!-- include spec files here... -->
|
||||||
@ -52,6 +53,7 @@
|
|||||||
<script src="parser_spec.js"></script>
|
<script src="parser_spec.js"></script>
|
||||||
<script src="api_spec.js"></script>
|
<script src="api_spec.js"></script>
|
||||||
<script src="metadata_spec.js"></script>
|
<script src="metadata_spec.js"></script>
|
||||||
|
<script src="ui_utils_spec.js"></script>
|
||||||
<script src="util_spec.js"></script>
|
<script src="util_spec.js"></script>
|
||||||
<script src="cmap_spec.js"></script>
|
<script src="cmap_spec.js"></script>
|
||||||
<script>
|
<script>
|
||||||
|
@ -160,13 +160,10 @@ function watchScroll(viewAreaElement, callback) {
|
|||||||
|
|
||||||
var currentY = viewAreaElement.scrollTop;
|
var currentY = viewAreaElement.scrollTop;
|
||||||
var lastY = state.lastY;
|
var lastY = state.lastY;
|
||||||
if (currentY > lastY) {
|
if (currentY !== lastY) {
|
||||||
state.down = true;
|
state.down = currentY > lastY;
|
||||||
} else if (currentY < lastY) {
|
|
||||||
state.down = false;
|
|
||||||
}
|
}
|
||||||
state.lastY = currentY;
|
state.lastY = currentY;
|
||||||
// else do nothing and use previous value
|
|
||||||
callback(state);
|
callback(state);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -182,6 +179,38 @@ function watchScroll(viewAreaElement, callback) {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use binary search to find the index of the first item in a given array which
|
||||||
|
* passes a given condition. The items are expected to be sorted in the sense
|
||||||
|
* that if the condition is true for one item in the array, then it is also true
|
||||||
|
* for all following items.
|
||||||
|
*
|
||||||
|
* @returns {Number} Index of the first array element to pass the test,
|
||||||
|
* or |items.length| if no such element exists.
|
||||||
|
*/
|
||||||
|
function binarySearchFirstItem(items, condition) {
|
||||||
|
var minIndex = 0;
|
||||||
|
var maxIndex = items.length - 1;
|
||||||
|
|
||||||
|
if (items.length === 0 || !condition(items[maxIndex])) {
|
||||||
|
return items.length;
|
||||||
|
}
|
||||||
|
if (condition(items[minIndex])) {
|
||||||
|
return minIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (minIndex < maxIndex) {
|
||||||
|
var currentIndex = (minIndex + maxIndex) >> 1;
|
||||||
|
var currentItem = items[currentIndex];
|
||||||
|
if (condition(currentItem)) {
|
||||||
|
maxIndex = currentIndex;
|
||||||
|
} else {
|
||||||
|
minIndex = currentIndex + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minIndex; /* === maxIndex */
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic helper to find out what elements are visible within a scroll pane.
|
* Generic helper to find out what elements are visible within a scroll pane.
|
||||||
*/
|
*/
|
||||||
@ -189,30 +218,45 @@ function getVisibleElements(scrollEl, views, sortByVisibility) {
|
|||||||
var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight;
|
var top = scrollEl.scrollTop, bottom = top + scrollEl.clientHeight;
|
||||||
var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth;
|
var left = scrollEl.scrollLeft, right = left + scrollEl.clientWidth;
|
||||||
|
|
||||||
var visible = [], view;
|
function isElementBottomBelowViewTop(view) {
|
||||||
|
var element = view.div;
|
||||||
|
var elementBottom =
|
||||||
|
element.offsetTop + element.clientTop + element.clientHeight;
|
||||||
|
return elementBottom > top;
|
||||||
|
}
|
||||||
|
|
||||||
|
var visible = [], view, element;
|
||||||
var currentHeight, viewHeight, hiddenHeight, percentHeight;
|
var currentHeight, viewHeight, hiddenHeight, percentHeight;
|
||||||
var currentWidth, viewWidth;
|
var currentWidth, viewWidth;
|
||||||
for (var i = 0, ii = views.length; i < ii; ++i) {
|
var firstVisibleElementInd = (views.length === 0) ? 0 :
|
||||||
|
binarySearchFirstItem(views, isElementBottomBelowViewTop);
|
||||||
|
|
||||||
|
for (var i = firstVisibleElementInd, ii = views.length; i < ii; i++) {
|
||||||
view = views[i];
|
view = views[i];
|
||||||
currentHeight = view.div.offsetTop + view.div.clientTop;
|
element = view.div;
|
||||||
viewHeight = view.div.clientHeight;
|
currentHeight = element.offsetTop + element.clientTop;
|
||||||
if ((currentHeight + viewHeight) < top) {
|
viewHeight = element.clientHeight;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (currentHeight > bottom) {
|
if (currentHeight > bottom) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
currentWidth = view.div.offsetLeft + view.div.clientLeft;
|
|
||||||
viewWidth = view.div.clientWidth;
|
currentWidth = element.offsetLeft + element.clientLeft;
|
||||||
if ((currentWidth + viewWidth) < left || currentWidth > right) {
|
viewWidth = element.clientWidth;
|
||||||
|
if (currentWidth + viewWidth < left || currentWidth > right) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
hiddenHeight = Math.max(0, top - currentHeight) +
|
hiddenHeight = Math.max(0, top - currentHeight) +
|
||||||
Math.max(0, currentHeight + viewHeight - bottom);
|
Math.max(0, currentHeight + viewHeight - bottom);
|
||||||
percentHeight = ((viewHeight - hiddenHeight) * 100 / viewHeight) | 0;
|
percentHeight = ((viewHeight - hiddenHeight) * 100 / viewHeight) | 0;
|
||||||
|
|
||||||
visible.push({ id: view.id, x: currentWidth, y: currentHeight,
|
visible.push({
|
||||||
view: view, percent: percentHeight });
|
id: view.id,
|
||||||
|
x: currentWidth,
|
||||||
|
y: currentHeight,
|
||||||
|
view: view,
|
||||||
|
percent: percentHeight
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var first = visible[0];
|
var first = visible[0];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user