1
+ <template >
2
+ <div
3
+ class =" lan-viewport"
4
+ ref =" viewport"
5
+ @scroll =" handleViewportScroll"
6
+ >
7
+ <div
8
+ class =" lan-scrollBar"
9
+ ref =" scrollBar"
10
+ ></div >
11
+ <div
12
+ class =" lan-scroll-list"
13
+ :style =" {transform:`translate3d(0, ${offset}px, 0)`}"
14
+ >
15
+ <div
16
+ v-for =" (item) in visibleData"
17
+ :key =" item.id"
18
+ :vid =" item.index"
19
+ ref =" items"
20
+ >
21
+ <slot :item =" item" ></slot >
22
+ </div >
23
+ </div >
24
+ </div >
25
+ </template >
26
+
27
+ <script >
28
+
29
+ export default {
30
+ components: {},
31
+ data () {
32
+ return {
33
+
34
+ };
35
+ },
36
+ props: {
37
+ titleInfos: {
38
+ type: Object ,
39
+ default : () => {
40
+ return {};
41
+ }
42
+ },
43
+ },
44
+ computed: {
45
+ prevCount () {
46
+ return Math .min (this .start , this .remain )
47
+ },
48
+ nextCount () {
49
+ return Math .min (this .items .length - this .end , this .remain )
50
+ },
51
+ formatData () {
52
+ return this .items .map ((item , index ) => ({ ... item, index }))
53
+ },
54
+ visibleData () {
55
+ let start = this .start - this .prevCount
56
+ let end = this .end + this .nextCount
57
+ return this .formatData .slice (start, end)
58
+ }
59
+ },
60
+ mounted () {
61
+ this .$refs .viewport .style .height = this .remain * this .size + ' px' // 设置viewPrort的高度
62
+ this .$refs .scrollBar .style .height = this .items .length * this .size + ' px' // 设置滚动条高度
63
+ this .end = this .start + this .remain // 计算显示范围
64
+ if (this .variable ) { // 表示高度不定
65
+ this .initPosition ()
66
+ }
67
+ },
68
+ watch: {},
69
+ methods: {
70
+ handleViewportScroll () {
71
+ let scrollTop = this .$refs .viewport .scrollTop
72
+ if (this .variable ) {
73
+ this .start = this .getStartIndex (scrollTop) // 算出开始的位置
74
+ this .end = this .start + this .remain
75
+ this .offset = this .positions [this .start - this .prevCount ] ? this .positions [this .start - this .prevCount ].top : 0
76
+ }
77
+ },
78
+ initPosition () { // 初始化位置
79
+ this .positions = this .items .map ((item , index ) => ({
80
+ index,
81
+ height: this .size ,
82
+ top: index * this .size ,
83
+ bottom: (index + 1 ) * this .size
84
+ }))
85
+ },
86
+ getStartIndex (value ) {
87
+ let start = 0
88
+ let end = this .positions .length
89
+ let temp = null
90
+ while (start < end) {
91
+ let middleIndex = parseInt ((start + end) / 2 )
92
+ let middleValue = this .positions [middleIndex].bottom
93
+ if (value == middleValue) {
94
+ return middleIndex + 1
95
+ } else if (middleValue < value) {
96
+ start = middleIndex + 1
97
+ } else if (middleValue > value) {
98
+ if (temp == null || temp > middleIndex) {
99
+ temp = middleIndex
100
+ }
101
+ end = middleIndex - 1
102
+ }
103
+ }
104
+ return temp
105
+ }
106
+ },
107
+ updated () {
108
+ this .$nextTick (() => { // 获取真实元素的位置 更新top和bottom
109
+ if (this .positions .length === 0 ) return
110
+ let nodes = this .$refs .items
111
+ if (! (nodes && nodes .length > 0 )) {
112
+ return
113
+ }
114
+ nodes .forEach (node => {
115
+ let rect = node .getBoundingClientRect ()
116
+ let height = rect .height
117
+ let index = + node .getAttribute (' vid' )
118
+ let oldHeight = this .positions [index].height
119
+ let val = oldHeight - height
120
+ if (val) {
121
+ // 先更新自己
122
+ this .positions [index].bottom = this .positions [index].bottom - val
123
+ this .positions [index].height = height
124
+ for (let i = index + 1 ; i < this .positions .length ; i++ ) { // 再更新后续兄弟
125
+ this .positions [i].top = this .positions [i - 1 ].bottom
126
+ this .positions [i].bottom = this .positions [i].bottom - val
127
+ }
128
+ }
129
+ })
130
+ this .$refs .scrollBar .style .height = this .positions [this .positions .length - 1 ].bottom + ' px'
131
+ // this.offset = this.positions[this.start - this.prevCount]? this.positions[this.start - this.prevCount].top : 0
132
+ })
133
+ },
134
+ mounted () {
135
+
136
+ },
137
+ }
138
+
139
+ </script >
140
+ <style lang='scss' scoped>
141
+ </style >
0 commit comments