Skip to content

Instantly share code, notes, and snippets.

@drozdzynski
Created June 22, 2018 07:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drozdzynski/95f859614eeab9c17ab6f8a797f1fd1b to your computer and use it in GitHub Desktop.
Save drozdzynski/95f859614eeab9c17ab6f8a797f1fd1b to your computer and use it in GitHub Desktop.
Vue RecycledGrid
<template>
<div class="items-wrapper" v-bind:style="{ height: wrapperHeight + 'px' }">
<div v-for="view of pool"
v-bind:key="view.index"
v-bind:style="{ transform: 'translateY(' + view.top + 'px) translateX(' + view.left + 'px)' }">
<slot
v-bind:item="view.item"
v-bind:index="view.index" />
</div>
<resize-observer v-on:notify="handleResize" />
</div>
</template>
<script lang="ts">
import Vue from '../vue'
import Component from 'vue-class-component'
import { ResizeObserver } from 'vue-resize'
@Component({
components: {
'resize-observer': ResizeObserver
},
props: {
items: {
type: Array,
required: true,
},
rowHeight: {
type: [Number, String],
default: null,
required: true,
},
cells: {
type: [Number, String],
default: 1,
},
cellWidth: {
type: [Number, String],
default: null,
},
scrollElement: {
default: () => document
}
}
})
export default class RecycledView extends Vue {
private $_ready: boolean = false
private scrollElement: HTMLElement
private items: any[]
private rowHeight: number
private cells: number
private cellWidth: number
private prefetch: number = 10
private pool: View[] = []
public mounted() {
this.$nextTick(() => {
this.updateVisibleItems()
this.$_ready = true
})
this.scrollElement.addEventListener('scroll', (event) => {
this.handleScroll(event)
})
this.$watch('items', this.updateVisibleItems)
}
public get rows(): number {
return Math.ceil(this.items.length / this.cells)
}
public get wrapperHeight(): number {
return this.rowHeight * this.rows
}
public handleResize() {
this.$emit('resize')
if (this.$_ready) this.updateVisibleItems()
}
public handleScroll(event) {
if (this.$_ready) this.updateVisibleItems()
}
private updateVisibleItems() {
this.$_ready = false
let scroll = {top: 0, bottom: 0}
let scrollElement = this.scrollElement instanceof Document ? this.scrollElement.documentElement : this.scrollElement
scroll.top = (scrollElement.scrollTop - this.$el.offsetTop),
scroll.top < 0 && (scroll.top = 0)
scroll.bottom = scroll.top + scrollElement.clientHeight
let startIndex = ~~(scroll.top / this.rowHeight) - this.prefetch
startIndex < 0 && (startIndex = 0)
let endIndex = Math.ceil(scroll.bottom / this.rowHeight) + this.prefetch
endIndex > this.rows && (endIndex = this.rows)
this.pool = []
let index = startIndex * this.cells;
for (let row = startIndex; row < endIndex; row++) {
for (let i = 0; i < this.cells; i++) {
index = index + 1
if(index >= this.items.length) {
this.$_ready = true
return
}
this.pool.push({
item: this.items[index],
index: index,
top: row * this.rowHeight,
left: this.cells > 1 ? this.cellWidth * i : 0,
})
}
}
this.$_ready = true
}
}
class View {
item: any
index: number
top: number
left: number
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment