| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- const BLOCK_ROWS = 50
- const CLUSTER_BLOCKS = 4
- class VirtualScroll {
- constructor (options) {
- this.rows = options.rows
- this.scrollEl = options.scrollEl
- this.contentEl = options.contentEl
- this.callback = options.callback
- this.cache = {}
- this.scrollTop = this.scrollEl.scrollTop
- this.initDOM(this.rows)
- this.scrollEl.scrollTop = this.scrollTop
- this.lastCluster = 0
- const onScroll = () => {
- if (this.lastCluster !== (this.lastCluster = this.getNum())) {
- this.initDOM(this.rows)
- this.callback()
- }
- }
- this.scrollEl.addEventListener('scroll', onScroll, false)
- this.destroy = () => {
- this.contentEl.innerHtml = ''
- this.scrollEl.removeEventListener('scroll', onScroll, false)
- }
- }
- initDOM (rows) {
- if (!this.clusterHeight) {
- this.cache.data = this.contentEl.innerHTML = rows[0] + rows[0] + rows[0]
- this.getRowsHeight(rows)
- }
- const data = this.initData(rows, this.getNum())
- const thisRows = data.rows.join('')
- const dataChanged = this.checkChanges('data', thisRows)
- const topOffsetChanged = this.checkChanges('top', data.topOffset)
- const bottomOffsetChanged = this.checkChanges('bottom', data.bottomOffset)
- const html = []
- if (dataChanged && topOffsetChanged) {
- if (data.topOffset) {
- html.push(this.getExtra('top', data.topOffset))
- }
- html.push(thisRows)
- if (data.bottomOffset) {
- html.push(this.getExtra('bottom', data.bottomOffset))
- }
- this.contentEl.innerHTML = html.join('')
- } else if (bottomOffsetChanged) {
- this.contentEl.lastChild.style.height = `${data.bottomOffset}px`
- }
- }
- getRowsHeight () {
- const nodes = this.contentEl.children
- const node = nodes[Math.floor(nodes.length / 2)]
- this.itemHeight = node.offsetHeight
- this.blockHeight = this.itemHeight * BLOCK_ROWS
- this.clusterRows = BLOCK_ROWS * CLUSTER_BLOCKS
- this.clusterHeight = this.blockHeight * CLUSTER_BLOCKS
- }
- getNum () {
- this.scrollTop = this.scrollEl.scrollTop
- return Math.floor(this.scrollTop / (this.clusterHeight - this.blockHeight)) || 0
- }
- initData (rows, num) {
- if (rows.length < BLOCK_ROWS) {
- return {
- topOffset: 0,
- bottomOffset: 0,
- rowsAbove: 0,
- rows
- }
- }
- const start = Math.max((this.clusterRows - BLOCK_ROWS) * num, 0)
- const end = start + this.clusterRows
- const topOffset = Math.max(start * this.itemHeight, 0)
- const bottomOffset = Math.max((rows.length - end) * this.itemHeight, 0)
- const thisRows = []
- let rowsAbove = start
- if (topOffset < 1) {
- rowsAbove++
- }
- for (let i = start; i < end; i++) {
- rows[i] && thisRows.push(rows[i])
- }
- return {
- topOffset,
- bottomOffset,
- rowsAbove,
- rows: thisRows
- }
- }
- checkChanges (type, value) {
- const changed = value !== this.cache[type]
- this.cache[type] = value
- return changed
- }
- getExtra (className, height) {
- const tag = document.createElement('tr')
- tag.className = `virtual-scroll-${className}`
- if (height) {
- tag.style.height = `${height}px`
- }
- return tag.outerHTML
- }
- }
- export default VirtualScroll
|