JSFiddle - React, Tailwind, and code Playground
by Lu Max
HTML
<script src="//unpkg.com/[email protected]/dist/vue.js"></script>
<script type="text/x-template" id="list-template">
<div
class="list-view"
ref="list"
@scroll="handleScroll">
<div
class="list-view-phantom"
:style="{
height: contentHeight
}">
</div>
<div
ref="content"
class="list-view-content">
<div
class="list-view-item"
:style="{
height: itemHeight + 'px'
}"
v-for="item in visibleData">
{{ item.value }}
</div>
</div>
</div>
</script>
<div id="app">
<template>
<list-view :data="data"></list-view>
</template>
</div>
SCSS
.list-view {
height: 400px;
overflow: auto;
position: relative;
color: #333;
border: 1px solid #aaa;
}
.list-view-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list-view-content {
left: 0;
right: 0;
top: 0;
position: absolute;
}
.list-view-item {
padding: 5px;
color: #666;
line-height: 30px;
box-sizing: border-box;
}
Babel + JSX
const ListView = {
name: 'ListView',
template: '#list-template',
props: {
data: {
type: Array,
required: true
},
itemHeight: {
type: Number,
default: 30
}
},
computed: {
contentHeight() {
return this.data.length * this.itemHeight + 'px';
}
},
mounted() {
this.updateVisibleData();
},
data() {
return {
visibleData: []
};
},
methods: {
updateVisibleData(scrollTop) {
scrollTop = scrollTop || 0;
const visibleCount = Math.ceil(this.$el.clientHeight / this.itemHeight);
const start = Math.floor(scrollTop / this.itemHeight);
const end = start + visibleCount;
this.visibleData = this.data.slice(start, end);
this.$refs.content.style.webkitTransform = `translate3d(0, ${ start * this.itemHeight }px, 0)`;
},
handleScroll() {
const scrollTop = this.$el.scrollTop;
this.updateVisibleData(scrollTop);
}
}
};
new Vue({
components: {
ListView
},
data() {
const data = [];
for (let i = 0; i < 10000; i++) {
data.push({ value: i });
}
return {
data
};
}
}).$mount('#app')