| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- <template>
- <view class="virtual-list" style="position: relative;">
- <movable-area style="position: absolute;right: 0;width: 30px;height: 100%;">
- <movable-view class="action-bar-box" direction="vertical" @change="change" :y="y" :animation="false">
- <view style="border-bottom: #000 solid 2px;width: 100%;"></view>
- <view style="border-bottom: #000 solid 2px;width: 100%;"></view>
- </movable-view>
- </movable-area>
- <scroll-view scroll-y="true" :style="{
- height: scrollHeight + 'px',
- position: 'relative',
- zIndex: 1
- }" @scroll="handleScroll" :scroll-top="scrollTop" :show-scrollbar="false">
- <view class="scroll-bar" :style="{
- height: localHeight + 'px'
- }"></view>
- <view class="list" :style="{
- transform: `translateY(${offset}px)`
- }">
- <view class="item-wrap" v-for="(item, index) in visibleData" :key="index">
- <slot :item="item" :active="active"></slot>
- </view>
- </view>
- </scroll-view>
- </view>
- </template>
- <script>
- export default {
- name: 'VirtualList',
- props: {
- // 所有的items
- items: Array,
- // 可视区域的item数量
- remain: Number,
- // item大小
- size: Number,
- // 当前章节
- active: Number,
- // 可使区域高度
- scrollHeight: Number
- },
- data() {
- return {
- // 起始
- start: 0,
- // 结束
- end: this.remain,
- // list 偏移量
- offset: 0,
- scrollTop: 0,
- y: 0
- };
- },
- created() {
- //当前章节滚动至顶部
- this.scrollTop = this.size * this.active;
- },
- computed: {
- // 预留项
- preCount() {
- return Math.min(this.start, this.remain);
- },
- nextCount() {
- return Math.min(this.items.length - this.end, this.remain);
- },
- // 可视区域的item
- visibleData() {
- const start = this.start - this.preCount;
- const end = this.end + this.nextCount;
- return this.items.slice(start, end);
- },
- localHeight() {
- return this.items.length * this.size;
- }
- },
- methods: {
- change(e) {
- if (e.detail.source !== 'touch') {
- return;
- }
- let y = e.detail.y;
- let scroll = (y / (this.scrollHeight - 40)) * (this.localHeight - this.scrollHeight);
- scroll = scroll < 0 ? 0 : scroll;
- this.scrollTop = scroll;
- },
- handleScroll(ev) {
- const scrollTop = ev.detail.scrollTop;
- this.y = (scrollTop / (this.localHeight - this.scrollHeight)) * (this.scrollHeight - 40);
- // 开始位置
- const start = Math.floor(scrollTop / this.size);
- this.start = start < 0 ? 0 : start;
- // 结束位置
- this.end = this.start + this.remain;
- // 计算偏移
- const offset = scrollTop - (scrollTop % this.size) - this.preCount * this.size;
- this.offset = offset < 0 ? 0 : offset;
- }
- }
- };
- </script>
- <style scoped>
- .list {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- }
- .action-bar-box {
- padding: 3px;
- display: flex;
- flex-flow: column;
- justify-content: space-around;
- align-items: center;
- position: absolute;
- right: 0;
- background-color: transparent;
- border-radius: 10rpx;
- box-shadow: 0 0 5px #000;
- width: 20px;
- height: 40px;
- z-index: 2;
- }
- </style>
|