43
43
</template >
44
44
45
45
<script >
46
- import Scroller from ' ../mixins/Scroller'
46
+ import { ResizeObserver } from ' vue-resize'
47
+ import { ObserveVisibility } from ' vue-observe-visibility'
48
+ import ScrollParent from ' scrollparent'
47
49
import config from ' ../config'
50
+ import { supportsPassive } from ' ../utils'
48
51
49
52
let uid = 0
50
53
51
54
export default {
52
55
name: ' RecycleScroller' ,
53
56
54
- mixins: [
55
- Scroller,
56
- ],
57
+ components: {
58
+ ResizeObserver,
59
+ },
60
+
61
+ directives: {
62
+ ObserveVisibility,
63
+ },
64
+
65
+ props: {
66
+
67
+ items: {
68
+ type: Array ,
69
+ required: true ,
70
+ },
71
+
72
+ itemHeight: {
73
+ type: Number ,
74
+ default: null ,
75
+ },
76
+
77
+ minItemHeight: {
78
+ type: [Number , String ],
79
+ default: null ,
80
+ },
81
+
82
+ heightField: {
83
+ type: String ,
84
+ default: ' height' ,
85
+ },
86
+
87
+ typeField: {
88
+ type: String ,
89
+ default: ' type' ,
90
+ },
91
+
92
+ keyField: {
93
+ type: String ,
94
+ default: ' id' ,
95
+ },
96
+
97
+ buffer: {
98
+ type: Number ,
99
+ default: 200 ,
100
+ },
101
+
102
+ pageMode: {
103
+ type: Boolean ,
104
+ default: false ,
105
+ },
106
+
107
+ prerender: {
108
+ type: Number ,
109
+ default: 0 ,
110
+ },
111
+
112
+ emitUpdate: {
113
+ type: Boolean ,
114
+ default: false ,
115
+ },
116
+ },
57
117
58
118
data () {
59
119
return {
@@ -64,6 +124,28 @@ export default {
64
124
}
65
125
},
66
126
127
+ computed: {
128
+ heights () {
129
+ if (this .itemHeight === null ) {
130
+ const heights = {
131
+ ' -1' : { accumulator: 0 },
132
+ }
133
+ const items = this .items
134
+ const field = this .heightField
135
+ const minItemHeight = this .minItemHeight
136
+ let accumulator = 0
137
+ let current
138
+ for (let i = 0 , l = items .length ; i < l; i++ ) {
139
+ current = items[i][field] || minItemHeight
140
+ accumulator += current
141
+ heights[i] = { accumulator, height: current }
142
+ }
143
+ return heights
144
+ }
145
+ return []
146
+ },
147
+ },
148
+
67
149
watch: {
68
150
items () {
69
151
this .updateVisibleItems (true )
@@ -102,6 +184,10 @@ export default {
102
184
})
103
185
},
104
186
187
+ beforeDestroy () {
188
+ this .removeListeners ()
189
+ },
190
+
105
191
methods: {
106
192
addView (pool , index , item , key , type ) {
107
193
const view = {
@@ -366,6 +452,94 @@ export default {
366
452
continuous,
367
453
}
368
454
},
455
+
456
+ getListenerTarget () {
457
+ let target = ScrollParent (this .$el )
458
+ // Fix global scroll target for Chrome and Safari
459
+ if (target === window .document .documentElement || target === window .document .body ) {
460
+ target = window
461
+ }
462
+ return target
463
+ },
464
+
465
+ getScroll () {
466
+ const el = this .$el
467
+ // const wrapper = this.$refs.wrapper
468
+ let scrollState
469
+
470
+ if (this .pageMode ) {
471
+ const size = el .getBoundingClientRect ()
472
+ let top = - size .top
473
+ let height = window .innerHeight
474
+ if (top < 0 ) {
475
+ height += top
476
+ top = 0
477
+ }
478
+ if (top + height > size .height ) {
479
+ height = size .height - top
480
+ }
481
+ scrollState = {
482
+ top,
483
+ bottom: top + height,
484
+ }
485
+ } else {
486
+ scrollState = {
487
+ top: el .scrollTop ,
488
+ bottom: el .scrollTop + el .clientHeight ,
489
+ }
490
+ }
491
+
492
+ return scrollState
493
+ },
494
+
495
+ applyPageMode () {
496
+ if (this .pageMode ) {
497
+ this .addListeners ()
498
+ } else {
499
+ this .removeListeners ()
500
+ }
501
+ },
502
+
503
+ addListeners () {
504
+ this .listenerTarget = this .getListenerTarget ()
505
+ this .listenerTarget .addEventListener (' scroll' , this .handleScroll , supportsPassive ? {
506
+ passive: true ,
507
+ } : false )
508
+ this .listenerTarget .addEventListener (' resize' , this .handleResize )
509
+ },
510
+
511
+ removeListeners () {
512
+ if (! this .listenerTarget ) {
513
+ return
514
+ }
515
+
516
+ this .listenerTarget .removeEventListener (' scroll' , this .handleScroll )
517
+ this .listenerTarget .removeEventListener (' resize' , this .handleResize )
518
+
519
+ this .listenerTarget = null
520
+ },
521
+
522
+ scrollToItem (index ) {
523
+ let scrollTop
524
+ if (this .itemHeight === null ) {
525
+ scrollTop = index > 0 ? this .heights [index - 1 ].accumulator : 0
526
+ } else {
527
+ scrollTop = index * this .itemHeight
528
+ }
529
+ this .scrollToPosition (scrollTop)
530
+ },
531
+
532
+ scrollToPosition (position ) {
533
+ this .$el .scrollTop = position
534
+ },
535
+
536
+ itemsLimitError () {
537
+ setTimeout (() => {
538
+ console .log (` It seems the scroller element isn't scrolling, so it tries to render all the items at once.` , ' Scroller:' , this .$el )
539
+ console .log (` Make sure the scroller has a fixed height and 'overflow-y' set to 'auto' so it can scroll correctly and only render the items visible in the scroll viewport.` )
540
+ })
541
+ throw new Error (' Rendered items limit reached' )
542
+ },
369
543
},
370
544
}
371
545
</script >
0 commit comments