|
55 | 55 | var height = this.size * this.remain
|
56 | 56 | var bench = this.bench || this.remain
|
57 | 57 | var keeps = this.remain + bench
|
58 |
| - var slots = this.$slots.default |
59 |
| - var total = (slots && slots.length) || 0 |
60 | 58 |
|
61 | 59 | this.delta = {
|
62 | 60 | start: start, // start index.
|
63 | 61 | end: start + keeps, // end index.
|
64 |
| - total: total, // all items count. |
65 | 62 | keeps: keeps, // nums keeping in real dom.
|
66 | 63 | bench: bench, // nums scroll pass should force update.
|
| 64 | + total: 0, // all items count, update in render filter. |
67 | 65 | offset: 0, // cache scrollTop offset.
|
68 | 66 | direct: 'd', // cache scroll direction.
|
69 | 67 | height: height, // container wrapper viewport height.
|
70 | 68 | fireTime: 0, // cache last event fire time avoid compact.
|
71 | 69 | paddingTop: 0, // container wrapper real padding-top.
|
72 | 70 | paddingBottom: 0, // container wrapper real padding-bottom.
|
73 | 71 | varCache: {}, // cache variable index height and padding offset.
|
74 |
| - averageSize: 0, // average/estimate item height before variable be calculated. |
75 |
| - lastCalcIndex: 0 // last calculated variable height/offset index, always increase. |
| 72 | + varAverSize: 0, // average/estimate item height before variable be calculated. |
| 73 | + varLastCalcIndex: 0 // last calculated variable height/offset index, always increase. |
76 | 74 | }
|
77 | 75 | },
|
78 | 76 |
|
79 | 77 | mounted: function () {
|
80 |
| - if (this.start && this.validStart(this.start)) { |
81 |
| - this.setScrollTop(this.variable ? this.getVarOffset(this.start) : this.start * this.size) |
| 78 | + if (this.start) { |
| 79 | + var start = this.getZone(this.start).start |
| 80 | + this.setScrollTop(this.variable ? this.getVarOffset(start) : start * this.size) |
82 | 81 | }
|
83 | 82 | },
|
84 | 83 |
|
85 | 84 | watch: {
|
86 | 85 | start: function (index) {
|
87 |
| - if (!this.validStart(index)) { |
88 |
| - return |
89 |
| - } |
90 |
| - |
91 | 86 | var delta = this.delta
|
92 |
| - var start, end, scrollTop |
93 |
| - var isOver = this.isOverflow(index) |
94 |
| - if (isOver) { |
95 |
| - var zone = this.getLastZone() |
96 |
| - end = zone.end |
97 |
| - start = zone.start |
98 |
| - scrollTop = delta.total * this.size |
99 |
| - } else { |
100 |
| - start = index |
101 |
| - end = start + delta.keeps |
102 |
| - scrollTop = start * this.size |
103 |
| - } |
| 87 | + var zone = this.getZone(index) |
104 | 88 |
|
105 |
| - if (this.variable) { |
106 |
| - scrollTop = this.getVarOffset(isOver ? delta.total : start) |
107 |
| - } |
| 89 | + var scrollTop = this.variable |
| 90 | + ? this.getVarOffset(zone.overflow ? delta.total : zone.start) |
| 91 | + : zone.overflow ? delta.total * this.size : zone.start * this.size |
108 | 92 |
|
109 |
| - delta.end = end |
110 |
| - delta.start = start >= this.remain ? start : 0 |
| 93 | + delta.end = zone.end |
| 94 | + delta.start = zone.start >= this.remain ? zone.start : 0 |
111 | 95 |
|
112 | 96 | this.$forceUpdate()
|
113 | 97 | Vue2.nextTick(this.setScrollTop.bind(this, scrollTop))
|
|
116 | 100 |
|
117 | 101 | methods: {
|
118 | 102 | onScroll: function (e) {
|
119 |
| - var scrollTop = this.$refs.vsl.scrollTop |
| 103 | + var delta = this.delta |
| 104 | + var offset = this.$refs.vsl.scrollTop |
120 | 105 |
|
121 |
| - this.updateZone(scrollTop) |
| 106 | + delta.direct = delta.offset > offset ? 'u' : 'd' |
| 107 | + delta.offset = offset |
122 | 108 |
|
123 |
| - if (this.onscroll) { |
124 |
| - this.onscroll(e, scrollTop) |
| 109 | + if (!offset && delta.total) { |
| 110 | + this.triggerEvent('totop') |
125 | 111 | }
|
126 |
| - }, |
127 | 112 |
|
128 |
| - updateZone: function (offset) { |
129 |
| - var delta = this.delta |
| 113 | + this.updateZone(offset) |
130 | 114 |
|
131 |
| - if (!offset && delta.total) { |
132 |
| - this.fireEvent('totop') |
| 115 | + if (this.onscroll) { |
| 116 | + this.onscroll(e, { |
| 117 | + end: delta.end, |
| 118 | + start: delta.start, |
| 119 | + offset: offset |
| 120 | + }) |
133 | 121 | }
|
| 122 | + }, |
134 | 123 |
|
135 |
| - delta.direct = delta.offset > offset ? 'u' : 'd' |
136 |
| - delta.offset = offset |
137 |
| - |
| 124 | + // update render zone by moving offset. |
| 125 | + updateZone: function (offset) { |
138 | 126 | var overs
|
139 | 127 | if (this.variable) {
|
140 | 128 | overs = this.getVarOvers(offset)
|
141 | 129 | } else {
|
142 | 130 | overs = Math.floor(offset / this.size)
|
143 | 131 | }
|
144 | 132 |
|
145 |
| - // calculate the start and end by moving items. |
146 |
| - var start = overs || 0 |
147 |
| - var end = overs ? (overs + delta.keeps) : delta.keeps |
148 |
| - |
149 |
| - var isOver = this.isOverflow(start) |
150 |
| - if (isOver) { |
151 |
| - var zone = this.getLastZone() |
152 |
| - end = zone.end |
153 |
| - start = zone.start |
154 |
| - } |
| 133 | + var delta = this.delta |
| 134 | + var zone = this.getZone(overs) |
155 | 135 |
|
156 | 136 | // for better performance, if scroll pass items within now bench, do not update.
|
157 |
| - if (!isOver && (overs > delta.start) && (overs - delta.start <= delta.bench)) { |
| 137 | + if (!zone.overflow && (overs > delta.start) && (overs - delta.start <= delta.bench)) { |
158 | 138 | return
|
159 | 139 | }
|
160 | 140 |
|
161 |
| - delta.end = end |
162 |
| - delta.start = start |
| 141 | + delta.end = zone.end |
| 142 | + delta.start = zone.start |
163 | 143 | this.$forceUpdate()
|
164 | 144 | },
|
165 | 145 |
|
|
175 | 155 | middle = low + Math.floor((high - low) / 2)
|
176 | 156 | middleOffset = this.getVarOffset(middle)
|
177 | 157 |
|
178 |
| - // calculate the averageSize at first binary search. |
179 |
| - if (!delta.averageSize) { |
180 |
| - delta.averageSize = Math.floor(middleOffset / middle) |
| 158 | + // calculate the variable average size at first binary search. |
| 159 | + if (!delta.varAverSize) { |
| 160 | + delta.varAverSize = Math.floor(middleOffset / middle) |
181 | 161 | }
|
182 | 162 |
|
183 | 163 | if (middleOffset === offset) {
|
|
211 | 191 | offset += size
|
212 | 192 | }
|
213 | 193 |
|
214 |
| - delta.lastCalcIndex = Math.max(delta.lastCalcIndex, index) |
215 |
| - delta.lastCalcIndex = Math.min(delta.lastCalcIndex, delta.total - 1) |
| 194 | + delta.varLastCalcIndex = Math.max(delta.varLastCalcIndex, index) |
| 195 | + delta.varLastCalcIndex = Math.min(delta.varLastCalcIndex, delta.total - 1) |
216 | 196 |
|
217 | 197 | return offset
|
218 | 198 | },
|
|
223 | 203 | return (cache && cache.size) || this.variable(index) || 0
|
224 | 204 | },
|
225 | 205 |
|
226 |
| - // return the paddingBottom when variable height base current zone. |
| 206 | + // return the variable paddingBottom base current zone. |
227 | 207 | getVarPaddingBottom () {
|
228 | 208 | var delta = this.delta
|
229 |
| - if (delta.total - delta.end <= delta.keeps || delta.lastCalcIndex === delta.total - 1) { |
| 209 | + if (delta.total - delta.end <= delta.keeps || delta.varLastCalcIndex === delta.total - 1) { |
230 | 210 | return this.getVarOffset(delta.total) - this.getVarOffset(delta.end)
|
231 | 211 | } else {
|
232 | 212 | // if unreached last zone or uncalculate real behind offset
|
233 | 213 | // return the estimate paddingBottom avoid too much calculate.
|
234 |
| - return (delta.total - delta.end) * (delta.averageSize || this.size) |
| 214 | + return (delta.total - delta.end) * (delta.varAverSize || this.size) |
235 | 215 | }
|
236 | 216 | },
|
237 | 217 |
|
|
240 | 220 | var delta = this.delta
|
241 | 221 | var overflow = (delta.total - delta.keeps > 0) && (start + this.remain >= delta.total)
|
242 | 222 | if (overflow && delta.direct === 'd') {
|
243 |
| - this.fireEvent('tobottom') |
| 223 | + this.triggerEvent('tobottom') |
244 | 224 | }
|
245 | 225 | return overflow
|
246 | 226 | },
|
247 | 227 |
|
248 |
| - // if overflow range return the last zone. |
249 |
| - getLastZone: function () { |
250 |
| - return { |
251 |
| - end: this.delta.total, |
252 |
| - start: this.delta.total - this.delta.keeps |
253 |
| - } |
254 |
| - }, |
255 |
| - |
256 |
| - // fire a props event to parent. |
257 |
| - fireEvent: function (event) { |
| 228 | + // trigger a props event on parent. |
| 229 | + triggerEvent: function (event) { |
258 | 230 | var now = +new Date()
|
259 | 231 | var delta = this.delta
|
260 | 232 | if (this[event] && now - delta.fireTime > 30) {
|
| 233 | + this[event]() |
261 | 234 | delta.fireTime = now
|
262 |
| - this[event](delta.start, delta.end) |
263 | 235 | }
|
264 | 236 | },
|
265 | 237 |
|
266 |
| - // check if given start is valid. |
267 |
| - validStart: function (start) { |
268 |
| - return start === parseInt(start, 10) && (start >= 0 && start < this.delta.total) |
| 238 | + // return the right zone info base on start/index. |
| 239 | + getZone (index) { |
| 240 | + var start, end |
| 241 | + var delta = this.delta |
| 242 | + |
| 243 | + index = parseInt(index, 10) || 0 |
| 244 | + index = index >= delta.total ? (delta.total - 1) : (index < 0 ? 0 : index) |
| 245 | + |
| 246 | + var overflow = this.isOverflow(index) |
| 247 | + // if overflow range return the last zone. |
| 248 | + if (overflow) { |
| 249 | + end = delta.total |
| 250 | + start = delta.total - delta.keeps |
| 251 | + } else { |
| 252 | + start = index |
| 253 | + end = start + delta.keeps |
| 254 | + } |
| 255 | + |
| 256 | + return { |
| 257 | + end: end, |
| 258 | + start: start, |
| 259 | + overflow: overflow |
| 260 | + } |
269 | 261 | },
|
270 | 262 |
|
271 | 263 | // set manual scrollTop.
|
|
0 commit comments