use rIC in pseudo virtual list

This commit is contained in:
Nolan Lawson 2018-01-30 21:17:01 -08:00
parent 03b055ab20
commit d1620c3bca
6 changed files with 73 additions and 7 deletions

5
package-lock.json generated
View file

@ -7244,6 +7244,11 @@
"setimmediate": "1.0.5" "setimmediate": "1.0.5"
} }
}, },
"tiny-queue": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tiny-queue/-/tiny-queue-0.2.1.tgz",
"integrity": "sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY="
},
"tmp": { "tmp": {
"version": "0.0.31", "version": "0.0.31",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz",

View file

@ -53,6 +53,7 @@
"svelte-loader": "^2.3.3", "svelte-loader": "^2.3.3",
"svelte-transitions": "^1.1.1", "svelte-transitions": "^1.1.1",
"svgo": "^1.0.3", "svgo": "^1.0.3",
"tiny-queue": "^0.2.1",
"uglifyjs-webpack-plugin": "^1.1.5", "uglifyjs-webpack-plugin": "^1.1.5",
"url-search-params": "^0.10.0", "url-search-params": "^0.10.0",
"webpack": "^3.10.0", "webpack": "^3.10.0",

View file

@ -7,7 +7,8 @@
makeProps="{{makeProps}}" makeProps="{{makeProps}}"
key="{{wrappedItem.item}}" key="{{wrappedItem.item}}"
intersectionObserver="{{intersectionObserver}}" intersectionObserver="{{intersectionObserver}}"
hide="{{shouldHide(wrappedItem.item, $intersectionStates)}}" isIntersecting="{{isIntersecting(wrappedItem.item, $intersectionStates)}}"
isCached="{{isCached(wrappedItem.item, $intersectionStates)}}"
height="{{getHeight(wrappedItem.item, $intersectionStates)}}" height="{{getHeight(wrappedItem.item, $intersectionStates)}}"
/> />
{{/each}} {{/each}}
@ -35,7 +36,7 @@
let intersectionStates = this.store.get('intersectionStates') let intersectionStates = this.store.get('intersectionStates')
let keys = Object.keys(intersectionStates) let keys = Object.keys(intersectionStates)
for (let key of keys) { for (let key of keys) {
intersectionStates[key].isIntersecting = false intersectionStates[key].isCached = true
} }
this.store.setForRealm({intersectionStates: intersectionStates}) this.store.setForRealm({intersectionStates: intersectionStates})
@ -60,8 +61,11 @@
} }
}, },
helpers: { helpers: {
shouldHide(key, $intersectionStates) { isIntersecting(key, $intersectionStates) {
return !!($intersectionStates[key] && !$intersectionStates[key].isIntersecting) return !!($intersectionStates[key] && $intersectionStates[key].isIntersecting)
},
isCached(key, $intersectionStates) {
return !!($intersectionStates[key] && $intersectionStates[key].isCached)
}, },
getHeight(key, $intersectionStates) { getHeight(key, $intersectionStates) {
return $intersectionStates[key] && $intersectionStates[key].rect.height return $intersectionStates[key] && $intersectionStates[key].rect.height

View file

@ -1,9 +1,9 @@
<div class="pseudo-virtual-list-item" <div class="pseudo-virtual-list-item"
aria-hidden="{{hide}}" aria-hidden="{{hide}}"
pseudo-virtual-list-key="{{key}}" pseudo-virtual-list-key="{{key}}"
style="height: {{hide ? `${height}px` : ''}};" style="height: {{shouldHide ? `${height}px` : ''}};"
ref:node> ref:node>
{{#if !hide}} {{#if !shouldHide}}
<:Component {component} <:Component {component}
virtualProps="{{props}}" virtualProps="{{props}}"
virtualIndex="{{index}}" virtualIndex="{{index}}"
@ -13,10 +13,38 @@
</div> </div>
<script> <script>
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
import { mark, stop } from '../../_utils/marks'
export default { export default {
oncreate() { oncreate() {
this.observe('isIntersecting', isIntersecting => {
if (isIntersecting) {
mark('render')
this.set({hide: false})
stop('render')
} else {
// unrender lazily; it's not a critical UI task
scheduleIdleTask(() => {
mark('unrender')
if (!this.get('isIntersecting')) {
this.set({hide: true})
}
stop('unrender')
})
}
})
let intersectionObserver = this.get('intersectionObserver') let intersectionObserver = this.get('intersectionObserver')
intersectionObserver.observe(this.refs.node) intersectionObserver.observe(this.refs.node)
},
computed: {
shouldHide: (isIntersecting, isCached, hide) => {
if (isCached) {
return true // if it's cached, always unrender immediately until proven it's intersecting
}
return !isIntersecting && hide
}
} }
} }
</script> </script>

View file

@ -5,7 +5,8 @@
:index :index
:scrollToThisItem :scrollToThisItem
:intersectionObserver :intersectionObserver
:hide :isIntersecting
:isCached
:height :height
on:scrollToPosition on:scrollToPosition
/> />

View file

@ -0,0 +1,27 @@
// Wrapper to call requestIdleCallback() to schedule low-priority work.
// See https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API
// for a good breakdown of the concepts behind this.
import Queue from 'tiny-queue'
const taskQueue = new Queue()
let runningRequestIdleCallback = false
function runTasks(deadline) {
while (taskQueue.length && deadline.timeRemaining() > 0) {
taskQueue.shift()()
}
if (taskQueue.length) {
requestIdleCallback(runTasks)
} else {
runningRequestIdleCallback = false
}
}
export function scheduleIdleTask(task) {
taskQueue.push(task)
if (!runningRequestIdleCallback) {
runningRequestIdleCallback = true
requestIdleCallback(runTasks)
}
}