1
1
import Vue from 'vue' ;
2
- import { throttle } from './util' ;
3
2
4
- Vue . component ( 'virtual-list' , {
3
+ const VirtualList = Vue . component ( 'vue- virtual-scroll -list' , {
5
4
6
5
props : {
7
- unit : {
6
+ itemHeight : {
8
7
type : Number ,
9
8
required : true
10
9
} ,
11
- remain : {
12
- type : Number ,
13
- required : true
14
- } ,
15
- amount : {
10
+ remainItems : {
16
11
type : Number ,
17
12
required : true
18
13
}
19
14
} ,
20
15
21
16
// an object helping to calculate
22
17
delta : {
23
- // scroll
24
- direct : '' ,
25
- last_top : 0 ,
26
- page_type : '' ,
27
- // data
28
- total : 0 ,
29
- joints : 0 ,
30
- start_index : 0 ,
31
- // style
32
- view_height : 0 ,
33
- all_padding : 0 ,
34
- padding_top : 0 ,
35
- bench_padding : 0
18
+ start : 0 , // start index
19
+ end : 0 , // end index
20
+ keeps : 0 , // nums of item keeping in real dom
21
+ total : 0 , // all items count
22
+ viewHeight : 0 , // viewport height
23
+ allPadding : 0 , // all padding of not-render-yet doms
24
+ paddingTop : 0 // container real padding-top
36
25
} ,
37
26
38
27
methods : {
39
- onScroll : throttle ( function ( ) {
40
- let delta = this . $options . delta ;
41
- let scrollTop = this . $refs . container . scrollTop ;
42
- let listHeight = this . $refs . listbox . offsetHeight ;
43
-
44
- this . saveDirect ( scrollTop ) ;
45
-
46
- // scroll to top
47
- if ( scrollTop === 0 ) {
48
- this . $emit ( 'toTop' ) ;
49
- }
50
-
51
- // scroll to bottom
52
- let paddingBottom = delta . all_padding - delta . padding_top ;
53
- if ( listHeight <= scrollTop + delta . view_height + paddingBottom ) {
54
- this . showNext ( ) ;
55
- }
56
-
57
- if ( delta . direct === 'UP' && scrollTop < delta . padding_top ) {
58
- this . showPrev ( ) ;
59
- }
60
- } , 10 , true , true ) ,
61
-
62
- saveDirect ( scrollTop ) {
63
- let delta = this . $options . delta ;
64
-
65
- if ( ! delta . last_top ) {
66
- delta . last_top = scrollTop ;
67
- } else {
68
- delta . direct = delta . last_top > scrollTop ? 'UP' : 'DOWN' ;
69
- delta . last_top = scrollTop ;
70
- }
28
+ onScroll ( ) {
29
+ this . updateZone ( this . $refs . container . scrollTop ) ;
71
30
} ,
72
31
73
- showNext ( ) {
32
+ updateZone ( offset ) {
74
33
let delta = this . $options . delta ;
34
+ let overs = Math . floor ( offset / this . itemHeight ) ;
75
35
76
- delta . page_type = 'NEXT' ;
77
- if ( delta . total - delta . start_index <= this . amount ) {
78
- this . $emit ( 'toBottom' ) ;
79
- } else {
80
- delta . start_index = delta . start_index + this . amount ;
81
- this . $forceUpdate ( ) ;
36
+ // need moving items at lease one unit height
37
+ // @todo : consider prolong the zone range size
38
+ let start = overs ? overs : 0 ;
39
+ let end = overs ? ( overs + delta . keeps ) : delta . keeps ;
40
+
41
+ if ( overs + this . remainItems >= delta . total ) {
42
+ end = delta . total ;
43
+ start = delta . total - delta . keeps ;
82
44
}
83
- } ,
84
45
85
- showPrev ( ) {
86
- this . $options . delta . page_type = 'PREV' ;
46
+ delta . end = end ;
47
+ delta . start = start ;
48
+
49
+ // call component to update items
87
50
this . $forceUpdate ( ) ;
88
- this . $emit ( 'toPrev' ) ;
89
51
} ,
90
52
91
53
filter ( items ) {
92
- let length = items . length ;
93
54
let delta = this . $options . delta ;
94
- let nowStartIndex , udf , list = [ ] ;
95
-
96
- if ( ! delta . total ) {
97
- delta . total = length ;
98
- }
99
-
100
- if ( delta . page_type === 'PREV' ) {
101
- // already the first page
102
- if ( delta . start_index === 0 ) {
103
- list = items . filter ( ( item , index ) => {
104
- return index >= 0 && index < this . amount ;
105
- } ) ;
106
- } else {
107
- list = items . filter ( ( item , index ) => {
108
- if ( index === delta . start_index - this . amount ) {
109
- nowStartIndex = index ;
110
- }
111
-
112
- return index >= ( delta . start_index - this . amount )
113
- && index < delta . start_index ;
114
- } ) ;
115
-
116
- if ( nowStartIndex !== udf ) {
117
- delta . start_index = nowStartIndex ;
118
- }
119
- }
120
-
121
- delta . padding_top = delta . start_index * this . unit ;
122
- } else {
123
- // flipping next or first render
124
-
125
- // virtual list has no any increase
126
- // just flip to next page from start index
127
- if ( length === delta . total ) {
128
- list = items . filter ( ( item , index ) => {
129
- return index >= delta . start_index
130
- && index < delta . start_index + this . amount ;
131
- } ) ;
132
- } else {
133
- list = items . filter ( ( item , index ) => {
134
- if ( index === delta . start_index + this . amount ) {
135
- nowStartIndex = index ;
136
- }
137
-
138
- return index >= ( delta . start_index + this . amount )
139
- && index < ( delta . start_index + this . amount * 2 ) ;
140
- } ) ;
141
-
142
- if ( nowStartIndex !== udf ) {
143
- delta . start_index = nowStartIndex ;
144
- }
145
-
146
- // save virtual list new length
147
- delta . total = length ;
148
- }
149
55
150
- // all padding pixel, include top and bottom
151
- // except amount and calculate when component update
152
- delta . all_padding = ( length - this . amount ) * this . unit ;
153
- // padding-top piexl
154
- delta . padding_top = delta . start_index * this . unit ;
155
- }
56
+ delta . total = items . length ;
57
+ delta . paddingTop = this . itemHeight * delta . start ;
58
+ delta . allPadding = this . itemHeight * ( items . length - delta . keeps ) ;
156
59
157
- return list ;
60
+ return items . filter ( ( item , index ) => {
61
+ return index >= delta . start && index <= delta . end ;
62
+ } ) ;
158
63
}
159
64
} ,
160
65
161
66
beforeMount ( ) {
67
+ let remains = this . remainItems ;
162
68
let delta = this . $options . delta ;
163
- delta . joints = Math . ceil ( this . remain / 2 ) ;
164
- delta . view_height = this . remain * this . unit ;
165
- delta . bench_padding = delta . joints * this . unit ;
166
- } ,
69
+ let bench = Math . ceil ( remains / 2 ) ;
167
70
168
- updated ( ) {
169
- window . requestAnimationFrame ( ( ) => {
170
- let delta = this . $options . delta ;
171
- this . $refs . container . scrollTop = delta . padding_top + delta . bench_padding ;
172
- } ) ;
71
+ delta . end = remains + bench ;
72
+ delta . keeps = remains + bench ;
73
+ delta . viewHeight = this . itemHeight * remains ;
173
74
} ,
174
75
175
76
render ( createElement ) {
176
- let delta = this . $options . delta ;
177
77
let showList = this . filter ( this . $slots . default ) ;
78
+ let { viewHeight, paddingTop, allPadding } = this . $options . delta ;
178
79
179
80
return createElement ( 'div' , {
180
81
'ref' : 'container' ,
181
82
'class' : 'virtual-list' ,
182
83
'style' : {
183
84
'overflow-y' : 'auto' ,
184
- 'height' : delta . view_height + 'px'
85
+ 'height' : viewHeight + 'px'
185
86
} ,
186
87
'on' : {
187
88
'scroll' : this . onScroll
@@ -194,11 +95,13 @@ Vue.component('virtual-list', {
194
95
createElement ( 'div' , {
195
96
'class' : 'virtual-list-box-padding' ,
196
97
'style' : {
197
- 'padding-top' : delta . padding_top + 'px' ,
198
- 'padding-bottom' : ( delta . all_padding - delta . padding_top ) + 'px'
98
+ 'padding-top' : paddingTop + 'px' ,
99
+ 'padding-bottom' : ( allPadding - paddingTop ) + 'px'
199
100
}
200
101
} , showList )
201
102
] )
202
103
] ) ;
203
104
}
204
105
} ) ;
106
+
107
+ export default VirtualList ;
0 commit comments