Slightly improved diff performance

This commit is contained in:
Eduard Urbach 2017-11-10 08:41:45 +01:00
parent d8367b6172
commit 44369cb916
5 changed files with 47 additions and 35 deletions

View File

@ -1,5 +1,4 @@
.anime-list-container .anime-list-container
vertical
width 100% width 100%
max-width 800px max-width 800px
margin 0 auto margin 0 auto

View File

@ -9,8 +9,11 @@ export function load(arn: AnimeNotifier, element: HTMLElement) {
// Diff // Diff
export function diff(arn: AnimeNotifier, element: HTMLElement) { export function diff(arn: AnimeNotifier, element: HTMLElement) {
let url = element.dataset.url || (element as HTMLAnchorElement).getAttribute("href") let url = element.dataset.url || (element as HTMLAnchorElement).getAttribute("href")
arn.diff(url) arn.diff(url)
.then(() => arn.scrollTo(element)) .then(() => {
// Avoid instant layout thrashing
arn.requestIdleCallback(() => arn.scrollTo(element))
})
.catch(console.error) .catch(console.error)
} }

View File

@ -294,6 +294,10 @@ export class AnimeNotifier {
} }
countUp() { countUp() {
if(!this.app.currentPath.includes("/paypal/success")) {
return
}
for(let element of findAll("count-up")) { for(let element of findAll("count-up")) {
let final = parseInt(element.innerText) let final = parseInt(element.innerText)
let duration = 2000.0 let duration = 2000.0
@ -384,9 +388,7 @@ export class AnimeNotifier {
return Promise.resolve(response) return Promise.resolve(response)
}) })
.then(response => response.text()) .then(response => response.text())
.then(html => { .then(html => Diff.root(document.documentElement, html))
Diff.root(document.documentElement, html)
})
.then(() => this.app.emit("DOMContentLoaded")) .then(() => this.app.emit("DOMContentLoaded"))
.then(() => this.loading(false)) // Because our loading element gets reset due to full page diff .then(() => this.loading(false)) // Because our loading element gets reset due to full page diff
} }
@ -605,9 +607,7 @@ export class AnimeNotifier {
let request = fetch(path, { let request = fetch(path, {
credentials: "same-origin" credentials: "same-origin"
}) })
.then(response => { .then(response => response.text())
return response.text()
})
history.pushState(url, null, url) history.pushState(url, null, url)
this.app.currentPath = url this.app.currentPath = url
@ -617,7 +617,7 @@ export class AnimeNotifier {
// Delay by transition-speed // Delay by transition-speed
return delay(200).then(() => request) return delay(200).then(() => request)
.then(html => this.app.setContent(html, true)) .then(html => Diff.innerHTML(this.app.content, html))
.then(() => this.app.emit("DOMContentLoaded")) .then(() => this.app.emit("DOMContentLoaded"))
.then(() => this.loading(false)) .then(() => this.loading(false))
.catch(console.error) .catch(console.error)
@ -664,9 +664,11 @@ export class AnimeNotifier {
const contentPadding = 24 const contentPadding = 24
let scrollHandle: number let scrollHandle: number
let oldScroll = this.app.content.parentElement.scrollTop
let newScroll = 0 let newScroll = 0
let finalScroll = Math.max(target.offsetTop - contentPadding, 0) let finalScroll = Math.max(target.offsetTop - contentPadding, 0)
// Calculating scrollTop will force a layout - careful!
let oldScroll = this.app.content.parentElement.scrollTop
let scrollDistance = finalScroll - oldScroll let scrollDistance = finalScroll - oldScroll
if(scrollDistance > 0 && scrollDistance < 4) { if(scrollDistance > 0 && scrollDistance < 4) {

View File

@ -39,7 +39,7 @@ export class Application {
this.lastRequest.abort() this.lastRequest.abort()
this.lastRequest = null this.lastRequest = null
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let request = new XMLHttpRequest() let request = new XMLHttpRequest()
@ -71,7 +71,7 @@ export class Application {
if(options.addToHistory === undefined) { if(options.addToHistory === undefined) {
options.addToHistory = true options.addToHistory = true
} }
// Set current path // Set current path
this.currentPath = url this.currentPath = url
@ -85,14 +85,14 @@ export class Application {
if(e.target !== this.content) { if(e.target !== this.content) {
return return
} }
// Remove listener after we finally got the correct event. // Remove listener after we finally got the correct event.
this.content.removeEventListener("transitionend", onTransitionEnd) this.content.removeEventListener("transitionend", onTransitionEnd)
// Wait for the network request to end. // Wait for the network request to end.
request.then(html => { request.then(html => {
// Set content // Set content
this.setContent(html, false) this.setContent(html)
this.scrollToTop() this.scrollToTop()
// Fade animations // Fade animations
@ -113,19 +113,15 @@ export class Application {
return request return request
} }
setContent(html: string, diff: boolean) { setContent(html: string) {
if(diff) { this.content.innerHTML = html
Diff.innerHTML(this.content, html)
} else {
this.content.innerHTML = html
}
} }
markActiveLinks(element?: HTMLElement) { markActiveLinks(element?: HTMLElement) {
if(element === undefined) if(element === undefined)
element = document.body element = document.body
let links = element.querySelectorAll("a") let links = element.getElementsByTagName("a")
for(let i = 0; i < links.length; i++) { for(let i = 0; i < links.length; i++) {
let link = links[i] let link = links[i]
@ -165,7 +161,7 @@ export class Application {
if(!url || url === self.currentPath) if(!url || url === self.currentPath)
return return
// Load requested page // Load requested page
self.load(url) self.load(url)
} }

View File

@ -7,23 +7,35 @@ export class Diff {
static rootContainer: HTMLElement static rootContainer: HTMLElement
// innerHTML will diff the element with the given HTML string and apply DOM mutations. // innerHTML will diff the element with the given HTML string and apply DOM mutations.
static innerHTML(aRoot: HTMLElement, html: string) { static innerHTML(aRoot: HTMLElement, html: string): Promise<void> {
if(!Diff.container) { if(!Diff.container) {
Diff.container = document.createElement("main") Diff.container = document.createElement("main")
} }
Diff.container.innerHTML = html Diff.container.innerHTML = html
Diff.childNodes(aRoot, Diff.container)
return new Promise((resolve, reject) => {
window.requestAnimationFrame(() => {
Diff.childNodes(aRoot, Diff.container)
resolve()
})
})
} }
// root will diff the document root element with the given HTML string and apply DOM mutations. // root will diff the document root element with the given HTML string and apply DOM mutations.
static root(aRoot: HTMLElement, html: string) { static root(aRoot: HTMLElement, html: string) {
if(!Diff.rootContainer) { return new Promise((resolve, reject) => {
Diff.rootContainer = document.createElement("html") if(!Diff.rootContainer) {
} Diff.rootContainer = document.createElement("html")
}
Diff.rootContainer.innerHTML = html.replace("<!DOCTYPE html>", "")
Diff.childNodes(aRoot.getElementsByTagName("body")[0], Diff.rootContainer.getElementsByTagName("body")[0]) Diff.rootContainer.innerHTML = html.replace("<!DOCTYPE html>", "")
window.requestAnimationFrame(() => {
Diff.childNodes(aRoot.getElementsByTagName("body")[0], Diff.rootContainer.getElementsByTagName("body")[0])
resolve()
})
})
} }
// childNodes diffs the child nodes of 2 given elements and applies DOM mutations. // childNodes diffs the child nodes of 2 given elements and applies DOM mutations.
@ -31,7 +43,7 @@ export class Diff {
let aChild = [...aRoot.childNodes] let aChild = [...aRoot.childNodes]
let bChild = [...bRoot.childNodes] let bChild = [...bRoot.childNodes]
let numNodes = Math.max(aChild.length, bChild.length) let numNodes = Math.max(aChild.length, bChild.length)
for(let i = 0; i < numNodes; i++) { for(let i = 0; i < numNodes; i++) {
let a = aChild[i] let a = aChild[i]
@ -69,7 +81,7 @@ export class Diff {
let elemB = b as HTMLElement let elemB = b as HTMLElement
let removeAttributes: Attr[] = [] let removeAttributes: Attr[] = []
for(let x = 0; x < elemA.attributes.length; x++) { for(let x = 0; x < elemA.attributes.length; x++) {
let attrib = elemA.attributes[x] let attrib = elemA.attributes[x]
@ -119,7 +131,7 @@ export class Diff {
continue continue
} }
elemA.setAttribute(attrib.name, attrib.value) elemA.setAttribute(attrib.name, attrib.value)
} }