-
Notifications
You must be signed in to change notification settings - Fork 116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update: Add Instant Preview loading overlay #350
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,7 +60,7 @@ | |
font-size: 12px; | ||
} | ||
|
||
// This crawler wrapper is used duing preview loading | ||
// This crawler wrapper is used during preview loading | ||
.bp-crawler-wrapper { | ||
position: absolute; | ||
top: 72px; | ||
|
@@ -99,3 +99,49 @@ | |
margin-left: 2px; | ||
} | ||
} | ||
|
||
@keyframes bp-spinner { | ||
0% { | ||
transform: translate(-50%, -50%) rotate(0deg); | ||
} | ||
|
||
100% { | ||
transform: translate(-50%, -50%) rotate(360deg); | ||
} | ||
} | ||
|
||
// Center spinner horizontally and vertically within container | ||
.bp-spinner { | ||
bottom: 0; | ||
left: 0; | ||
margin: auto; | ||
position: absolute; | ||
right: 0; | ||
top: 0; | ||
} | ||
|
||
.bp-spinner div, | ||
.bp-spinner div::after { | ||
border: 25px solid #0061d5; | ||
border-radius: 50%; | ||
border-top-color: transparent; | ||
height: 170px; | ||
position: absolute; | ||
width: 170px; | ||
} | ||
|
||
.bp-spinner div { | ||
animation: bp-spinner 1s linear infinite; | ||
left: 100px; | ||
top: 100px; | ||
} | ||
|
||
.bp-spinner div::after { | ||
transform: rotate(90deg); | ||
} | ||
|
||
.bp-spinner { | ||
height: 30px; | ||
transform: translate(-15px, -15px) scale(.15) translate(15px, 15px); | ||
width: 30px; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is what it looks like: |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,8 +10,10 @@ import Popup from '../../Popup'; | |
import RepStatus from '../../RepStatus'; | ||
import { | ||
CLASS_BOX_PREVIEW_FIND_BAR, | ||
CLASS_CRAWLER, | ||
CLASS_HIDDEN, | ||
CLASS_IS_SCROLLABLE, | ||
CLASS_SPINNER, | ||
DOC_STATIC_ASSETS_VERSION, | ||
PERMISSION_DOWNLOAD, | ||
PRELOAD_REP_NAME, | ||
|
@@ -577,6 +579,15 @@ class DocBaseViewer extends BaseViewer { | |
// Do not disable create object URL in IE11 or iOS Chrome - pdf.js issues #3977 and #8081 are | ||
// not applicable to Box's use case and disabling causes performance issues | ||
PDFJS.disableCreateObjectURL = false; | ||
|
||
// Customize pdf.js loading icon. We modify the prototype of PDFPageView to get around directly modifying | ||
// pdf_viewer.js | ||
const resetFunc = PDFJS.PDFPageView.prototype.reset; | ||
PDFJS.PDFPageView.prototype.reset = function reset(...args) { | ||
resetFunc.bind(this)(args); | ||
this.loadingIconDiv.classList.add(CLASS_SPINNER); | ||
this.loadingIconDiv.innerHTML = '<div></div>'; | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is so we don't have to go into pdf.js and modify their code after each upgrade. These are the relevant lines in pdf.js: https://github.com/mozilla/pdf.js/blob/1419b7ffe7fb34aa48002fd388d7a3848c668709/web/pdf_page_view.js#L183 |
||
} | ||
|
||
/** | ||
|
@@ -593,7 +604,7 @@ class DocBaseViewer extends BaseViewer { | |
printCheckmark.innerHTML = ICON_PRINT_CHECKMARK.trim(); | ||
|
||
const loadingIndicator = document.createElement('div'); | ||
loadingIndicator.classList.add('bp-crawler'); | ||
loadingIndicator.classList.add(CLASS_CRAWLER); | ||
loadingIndicator.innerHTML = ` | ||
<div></div> | ||
<div></div> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,9 +2,12 @@ import EventEmitter from 'events'; | |
import { | ||
CLASS_BOX_PREVIEW_PRELOAD, | ||
CLASS_BOX_PREVIEW_PRELOAD_CONTENT, | ||
CLASS_BOX_PREVIEW_PRELOAD_OVERLAY, | ||
CLASS_BOX_PREVIEW_PRELOAD_WRAPPER_DOCUMENT, | ||
CLASS_INVISIBLE, | ||
CLASS_PREVIEW_LOADED | ||
CLASS_IS_TRANSPARENT, | ||
CLASS_PREVIEW_LOADED, | ||
CLASS_SPINNER | ||
} from '../../constants'; | ||
import { get, setDimensions } from '../../util'; | ||
|
||
|
@@ -21,7 +24,33 @@ const NUM_PAGES_MAX = 500; // Don't show more than 500 placeholder pages | |
|
||
const ACCEPTABLE_RATIO_DIFFERENCE = 0.025; // Acceptable difference in ratio of PDF dimensions to image dimensions | ||
|
||
const SPINNER_HTML = `<div class="${CLASS_SPINNER}"><div></div></div>`; | ||
|
||
class DocPreloader extends EventEmitter { | ||
/** @property {HTMLElement} - Viewer container */ | ||
containerEl; | ||
|
||
/** @property {HTMLElement} - Preload image element */ | ||
imageEl; | ||
|
||
/** @property {HTMLElement} - Preload overlay element */ | ||
overlayEl; | ||
|
||
/** @property {HTMLElement} - Preload container element */ | ||
preloadEl; | ||
|
||
/** @property {PreviewUI} - Preview's UI instance */ | ||
previewUI; | ||
|
||
/** @property {string} - Preload representation content URL */ | ||
srcUrl; | ||
|
||
/** @property {string} - Class name for preload wrapper */ | ||
wrapperClassName; | ||
|
||
/** @property {HTMLElement} - Preload wrapper element */ | ||
wrapperEl; | ||
|
||
/** | ||
* [constructor] | ||
* | ||
|
@@ -59,12 +88,16 @@ class DocPreloader extends EventEmitter { | |
this.wrapperEl.innerHTML = ` | ||
<div class="${CLASS_BOX_PREVIEW_PRELOAD} ${CLASS_INVISIBLE}"> | ||
<img class="${CLASS_BOX_PREVIEW_PRELOAD_CONTENT}" src="${this.srcUrl}" /> | ||
<div class="${CLASS_BOX_PREVIEW_PRELOAD_CONTENT} ${CLASS_BOX_PREVIEW_PRELOAD_OVERLAY}"> | ||
${SPINNER_HTML} | ||
</div> | ||
</div> | ||
`.trim(); | ||
|
||
this.containerEl.appendChild(this.wrapperEl); | ||
this.preloadEl = this.wrapperEl.querySelector(`.${CLASS_BOX_PREVIEW_PRELOAD}`); | ||
this.imageEl = this.preloadEl.querySelector(`img.${CLASS_BOX_PREVIEW_PRELOAD_CONTENT}`); | ||
this.overlayEl = this.preloadEl.querySelector(`.${CLASS_BOX_PREVIEW_PRELOAD_OVERLAY}`); | ||
this.bindDOMListeners(); | ||
}); | ||
} | ||
|
@@ -82,13 +115,15 @@ class DocPreloader extends EventEmitter { | |
return; | ||
} | ||
|
||
// Set image dimensions | ||
// Set image and overlay dimensions | ||
setDimensions(this.imageEl, scaledWidth, scaledHeight); | ||
setDimensions(this.overlayEl, scaledWidth, scaledHeight); | ||
|
||
// Add and scale correct number of placeholder elements | ||
for (let i = 0; i < numPages - 1; i++) { | ||
const placeholderEl = document.createElement('div'); | ||
placeholderEl.className = CLASS_BOX_PREVIEW_PRELOAD_CONTENT; | ||
placeholderEl.innerHTML = SPINNER_HTML; | ||
setDimensions(placeholderEl, scaledWidth, scaledHeight); | ||
this.preloadEl.appendChild(placeholderEl); | ||
} | ||
|
@@ -113,18 +148,37 @@ class DocPreloader extends EventEmitter { | |
return; | ||
} | ||
|
||
this.restoreScrollPosition(); | ||
this.unbindDOMListeners(); | ||
this.restoreScrollPosition(); | ||
this.wrapperEl.classList.add(CLASS_IS_TRANSPARENT); | ||
|
||
// Cleanup preload DOM after fade out | ||
this.wrapperEl.addEventListener('transitionend', this.cleanupPreload); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here we fade out the preload image into the real page and then remove the preload elements. This seems to be a better experience than swapping out the preload instantaneously. |
||
|
||
// Cleanup preload DOM immediately if user scrolls after the document is ready since we don't want half-faded | ||
// out preload content to be on top of real document content while scrolling | ||
this.wrapperEl.addEventListener('scroll', this.cleanupPreload); | ||
} | ||
|
||
/** | ||
* Cleans up preload DOM. | ||
* | ||
* @private | ||
* @return {void} | ||
*/ | ||
cleanupPreload = () => { | ||
if (this.wrapperEl) { | ||
this.wrapperEl.parentNode.removeChild(this.wrapperEl); | ||
this.wrapperEl = undefined; | ||
} | ||
|
||
this.wrapperEl.parentNode.removeChild(this.wrapperEl); | ||
this.wrapperEl = undefined; | ||
this.preloadEl = undefined; | ||
this.imageEl = undefined; | ||
|
||
if (this.srcUrl) { | ||
URL.revokeObjectURL(this.srcUrl); | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Binds event listeners for preload | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using this absolute centering strategy: https://www.smashingmagazine.com/2013/08/absolute-horizontal-vertical-centering-css/