add custom solution for virtual scrolling to ease ram and cpu use when scrolling for a long time

This commit is contained in:
Shpuld Shpuldson 2020-01-15 15:17:05 +02:00
parent 7397636914
commit 9eae4d07c1
5 changed files with 57 additions and 9 deletions

View file

@ -35,7 +35,8 @@ const conversation = {
data () { data () {
return { return {
highlight: null, highlight: null,
expanded: false expanded: false,
height: '115px'
} }
}, },
props: [ props: [
@ -44,7 +45,8 @@ const conversation = {
'isPage', 'isPage',
'pinnedStatusIdsObject', 'pinnedStatusIdsObject',
'inProfile', 'inProfile',
'profileUserId' 'profileUserId',
'hidden'
], ],
created () { created () {
if (this.isPage) { if (this.isPage) {
@ -102,6 +104,9 @@ const conversation = {
}, },
isExpanded () { isExpanded () {
return this.expanded || this.isPage return this.expanded || this.isPage
},
hiderStyle () {
return this.hidden ? { height: this.height } : {}
} }
}, },
components: { components: {
@ -112,7 +117,7 @@ const conversation = {
const newConversationId = this.getConversationId(newVal) const newConversationId = this.getConversationId(newVal)
const oldConversationId = this.getConversationId(oldVal) const oldConversationId = this.getConversationId(oldVal)
if (newConversationId && oldConversationId && newConversationId === oldConversationId) { if (newConversationId && oldConversationId && newConversationId === oldConversationId) {
this.setHighlight(this.originalStatusId) this.setHighheightlight(this.originalStatusId)
} else { } else {
this.fetchConversation() this.fetchConversation()
} }
@ -121,6 +126,10 @@ const conversation = {
if (value) { if (value) {
this.fetchConversation() this.fetchConversation()
} }
},
hidden (value) {
this.height = `${this.$el.clientHeight}px`
console.log('Element height:', this.height)
} }
}, },
methods: { methods: {

View file

@ -1,10 +1,11 @@
<template> <template>
<div <div
:style="hiderStyle"
class="timeline panel-default" class="timeline panel-default"
:class="[isExpanded ? 'panel' : 'panel-disabled']" :class="[isExpanded ? 'panel' : 'panel-disabled']"
> >
<div <div
v-if="isExpanded" v-if="isExpanded && !hidden"
class="panel-heading conversation-heading" class="panel-heading conversation-heading"
> >
<span class="title"> {{ $t('timeline.conversation') }} </span> <span class="title"> {{ $t('timeline.conversation') }} </span>
@ -28,6 +29,7 @@
:replies="getReplies(status.id)" :replies="getReplies(status.id)"
:in-profile="inProfile" :in-profile="inProfile"
:profile-user-id="profileUserId" :profile-user-id="profileUserId"
:hidden="hidden"
class="status-fadein panel-body" class="status-fadein panel-body"
@goto="setHighlight" @goto="setHighlight"
@toggleExpanded="toggleExpanded" @toggleExpanded="toggleExpanded"

View file

@ -34,7 +34,8 @@ const Status = {
'inlineExpanded', 'inlineExpanded',
'showPinned', 'showPinned',
'inProfile', 'inProfile',
'profileUserId' 'profileUserId',
'hidden'
], ],
data () { data () {
return { return {
@ -121,7 +122,7 @@ const Status = {
return this.mergedConfig.hideFilteredStatuses return this.mergedConfig.hideFilteredStatuses
}, },
hideStatus () { hideStatus () {
return (this.hideReply || this.deleted) || (this.muted && this.hideFilteredStatuses) return (this.hideReply || this.deleted) || (this.muted && this.hideFilteredStatuses) || this.hidden
}, },
isFocused () { isFocused () {
// retweet or root of an expanded conversation // retweet or root of an expanded conversation

View file

@ -32,7 +32,8 @@ const Timeline = {
return { return {
paused: false, paused: false,
unfocused: false, unfocused: false,
bottomedOut: false bottomedOut: false,
vScrollIndex: 0
} }
}, },
computed: { computed: {
@ -68,6 +69,12 @@ const Timeline = {
}, },
pinnedStatusIdsObject () { pinnedStatusIdsObject () {
return keyBy(this.pinnedStatusIds) return keyBy(this.pinnedStatusIds)
},
displayingStatuses () {
const amount = this.timeline.visibleStatuses.length
const min = Math.max(0, this.vScrollIndex - 20)
const max = Math.min(amount, this.vScrollIndex + 20)
return this.timeline.visibleStatuses.slice(min, max).map(_ => _.id)
} }
}, },
components: { components: {
@ -79,7 +86,7 @@ const Timeline = {
const credentials = store.state.users.currentUser.credentials const credentials = store.state.users.currentUser.credentials
const showImmediately = this.timeline.visibleStatuses.length === 0 const showImmediately = this.timeline.visibleStatuses.length === 0
window.addEventListener('scroll', this.scrollLoad) window.addEventListener('scroll', throttle(this.scrollLoad, 100))
if (store.state.api.fetchers[this.timelineName]) { return false } if (store.state.api.fetchers[this.timelineName]) { return false }
@ -142,7 +149,35 @@ const Timeline = {
} }
}) })
}, 1000, this), }, 1000, this),
determineVisibleStatuses () {
const statuses = this.$refs.timeline.children
const bodyBRect = document.body.getBoundingClientRect()
const height = Math.max(bodyBRect.height, -(bodyBRect.y))
const centerOfScreen = window.pageYOffset + (window.innerHeight * 0.5)
// Approximate which status is in the middle of the screen and check how
// far it is roughly from the viewport
let approxIndex = Math.floor(statuses.length * (centerOfScreen / height))
let err = statuses[approxIndex].getBoundingClientRect().y
// if the status is too far from viewport, check the next/previous ones if
// they happen to be better
while (err < -100) {
approxIndex++
err = statuses[approxIndex].getBoundingClientRect().y
}
while (err > 1000) {
approxIndex--
err = statuses[approxIndex].getBoundingClientRect().y
}
// this status is now the center point for virtual scrolling
this.vScrollIndex = approxIndex
},
scrollLoad (e) { scrollLoad (e) {
this.determineVisibleStatuses()
const bodyBRect = document.body.getBoundingClientRect() const bodyBRect = document.body.getBoundingClientRect()
const height = Math.max(bodyBRect.height, -(bodyBRect.y)) const height = Math.max(bodyBRect.height, -(bodyBRect.y))
if (this.timeline.loading === false && if (this.timeline.loading === false &&

View file

@ -34,7 +34,7 @@
</div> </div>
</div> </div>
<div :class="classes.body"> <div :class="classes.body">
<div class="timeline"> <div ref="timeline" class="timeline">
<template v-for="statusId in pinnedStatusIds"> <template v-for="statusId in pinnedStatusIds">
<conversation <conversation
v-if="timeline.statusesObject[statusId]" v-if="timeline.statusesObject[statusId]"
@ -56,6 +56,7 @@
:collapsable="true" :collapsable="true"
:in-profile="inProfile" :in-profile="inProfile"
:profile-user-id="userId" :profile-user-id="userId"
:hidden="!displayingStatuses.includes(status.id)"
/> />
</template> </template>
</div> </div>