<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" /> <meta name="viewort" content="width=device-width, initial-sacle=1.0" />
<title>test</title>
<style>
.container { width: 600px; height: 500px; border: 1px solid red; margin: 150px auto; }
.fv-container { height: 100%; width: 100%; overflow: auto; }
.fv-list-item { height: 100px; width: 100%; border: 1px solid #ccc; box-sizing: border-box; text-align: center; line-height: 100px; font-size: 18px; font-weight: 700; }
</style>
</head>
<body>
<div class="container">
<div class="fv-container">
<div class="fv-list"> </div>
</div>
</div>
</body>
<script>
function throttle(fn, delay = 200) {
let start = 0;
return function (...args) {
const now = Date.now()
if (now - start >= delay) {
fn.apply(this, args) start = now
}
}
}
function rafThrottle(fn) {
let lock = false;
return function (...args) {
window.requestAnimationFrame(() => {
if (lock) return
lock = true;
fn.apply(this, args)
lock = false
})
}
}
class FvList {
constructor(containerClass, listClass) {
this.state = {
data: [],
itemHeight: 100,
viewheight: 0,
maxCount: 0,
}
this.startIndex = 0;
this.endIndex = 0;
this.renderList = [];
this.renderStyle = {};
this.oContainer = document.querySelector(containerClass);
this.oList = document.querySelector(listClass);
}
addData() {
for (let i = 0; i < 10; i++) {
const len = this.state.data.length
this.state.data.push(len + 1)
}
}
init() {
this.state.viewheight = this.oContainer.offsetHeight;
this.state.maxCount = Math.ceil(this.state.viewheight / this.state.itemHeight) + 1
this.addData();
this.render()
this.oContainer.addEventListener('scroll', rafThrottle(this.handelScroll.bind(this)))
}
handelScroll() {
const { scrollTop } = this.oContainer
this.startIndex = Math.floor(scrollTop /
this.state.itemHeight)
this.render()
if (this.endIndex >= this.state.data.length) {
this.addData();
}
}
computedIndex() {
const end = this.startIndex + this.state.maxCount;
this.endIndex = end < this.state.data.length ? end : this.state.data.length
}
computedList() {
this.renderList = this.state.data.slice(this.startIndex, this.endIndex)
}
computedStyle() {
this.renderStyle = {
height: `${this.state.data.length * this.state.itemHeight - this.state.itemHeight * this.startIndex}px`,
transform: `translate3d(0, ${this.startIndex * this.state.itemHeight}px, 0)`,
}
}
render() {
this.computedIndex()
this.computedList()
this.computedStyle()
const tpl = this.renderList.map(i => `<div class="fv-list-item">${i}</div>`).join('')
this.oList.innerHTML = tpl
this.oList.style.height = this.renderStyle.height
this.oList.style.transform = this.renderStyle.transform
}
}
const vList = new FvList('.fv-container', '.fv-list')
vList.init()
</script>
</html>