UNPKG

3.5 kBJavaScriptView Raw
1import Constants from '../constants/index.js'
2
3class VirtualScroll {
4
5 constructor (options) {
6 this.rows = options.rows
7 this.scrollEl = options.scrollEl
8 this.contentEl = options.contentEl
9 this.callback = options.callback
10
11 this.cache = {}
12 this.scrollTop = this.scrollEl.scrollTop
13
14 this.initDOM(this.rows)
15
16 this.scrollEl.scrollTop = this.scrollTop
17 this.lastCluster = 0
18
19 const onScroll = () => {
20 if (this.lastCluster !== (this.lastCluster = this.getNum())) {
21 this.initDOM(this.rows)
22 this.callback()
23 }
24 }
25
26 this.scrollEl.addEventListener('scroll', onScroll, false)
27 this.destroy = () => {
28 this.contentEl.innerHtml = ''
29 this.scrollEl.removeEventListener('scroll', onScroll, false)
30 }
31 }
32
33 initDOM (rows) {
34 if (typeof this.clusterHeight === 'undefined') {
35 this.cache.scrollTop = this.scrollEl.scrollTop
36 this.cache.data = this.contentEl.innerHTML = rows[0] + rows[0] + rows[0]
37 this.getRowsHeight(rows)
38 }
39
40 const data = this.initData(rows, this.getNum())
41 const thisRows = data.rows.join('')
42 const dataChanged = this.checkChanges('data', thisRows)
43 const topOffsetChanged = this.checkChanges('top', data.topOffset)
44 const bottomOffsetChanged = this.checkChanges('bottom', data.bottomOffset)
45 const html = []
46
47 if (dataChanged && topOffsetChanged) {
48 if (data.topOffset) {
49 html.push(this.getExtra('top', data.topOffset))
50 }
51 html.push(thisRows)
52 if (data.bottomOffset) {
53 html.push(this.getExtra('bottom', data.bottomOffset))
54 }
55 this.contentEl.innerHTML = html.join('')
56 } else if (bottomOffsetChanged) {
57 this.contentEl.lastChild.style.height = `${data.bottomOffset}px`
58 }
59 }
60
61 getRowsHeight () {
62 if (typeof this.itemHeight === 'undefined') {
63 const nodes = this.contentEl.children
64 const node = nodes[Math.floor(nodes.length / 2)]
65
66 this.itemHeight = node.offsetHeight
67 }
68 this.blockHeight = this.itemHeight * Constants.BLOCK_ROWS
69 this.clusterRows = Constants.BLOCK_ROWS * Constants.CLUSTER_BLOCKS
70 this.clusterHeight = this.blockHeight * Constants.CLUSTER_BLOCKS
71 }
72
73 getNum () {
74 this.scrollTop = this.scrollEl.scrollTop
75 return Math.floor(this.scrollTop / (this.clusterHeight - this.blockHeight)) || 0
76 }
77
78 initData (rows, num) {
79 if (rows.length < Constants.BLOCK_ROWS) {
80 return {
81 topOffset: 0,
82 bottomOffset: 0,
83 rowsAbove: 0,
84 rows
85 }
86 }
87 const start = Math.max((this.clusterRows - Constants.BLOCK_ROWS) * num, 0)
88 const end = start + this.clusterRows
89 const topOffset = Math.max(start * this.itemHeight, 0)
90 const bottomOffset = Math.max((rows.length - end) * this.itemHeight, 0)
91 const thisRows = []
92 let rowsAbove = start
93
94 if (topOffset < 1) {
95 rowsAbove++
96 }
97 for (let i = start; i < end; i++) {
98 rows[i] && thisRows.push(rows[i])
99 }
100
101 this.dataStart = start
102 this.dataEnd = end
103
104 return {
105 topOffset,
106 bottomOffset,
107 rowsAbove,
108 rows: thisRows
109 }
110 }
111
112 checkChanges (type, value) {
113 const changed = value !== this.cache[type]
114
115 this.cache[type] = value
116 return changed
117 }
118
119 getExtra (className, height) {
120 const tag = document.createElement('li')
121
122 tag.className = `virtual-scroll-${className}`
123 if (height) {
124 tag.style.height = `${height}px`
125 }
126 return tag.outerHTML
127 }
128}
129
130export default VirtualScroll