Как рассчитать видимые номера диапазона предметов для виртуального списка прокрутки?
Я попытался найти библиотеку, чтобы сделать это для меня, но я не нашел библиотеку, которая бы обладала необходимыми функциями или стилем, поэтому я решил попробовать свои силы самостоятельно; единственная проблема с этим - вычисление диапазона отображаемых элементов, которые в настоящее время видны в списке. Сам список работает практически без проблем, за исключением ранее описанного.
Основная проблема - математика, необходимая для определения этого диапазона, и чтобы эта математика работала для всех вариантов использования, где "все варианты использования" просто означают прокрутку списка, а числа, представляющие видимые элементы, остаются точными. В настоящее время числа в основном неточны, когда округлены в любом из трех возможных направлений; пол, ближайший и потолок.
Для демонстрации я удалил округление, чтобы проиллюстрировать ранее описанную проблему.
var comp = {
template: `
<div class="table-responsive container_table" :style="{height: viewHeight + 'px'}">
<table class="table table-bordered table_outer host" ref="table">
<thead>
<tr>
<th>Log</th>
</tr>
</thead>
<tbody>
<tr>
<td :colspan="1" ref="content">
<div class="td_scroll" :style="td_scrollHeight" @scroll="refresh()" ref="td_scroll">
<div class="total-padding" :style="{height:scrollHeight + 'px'}"></div>
<div class="scrollable-content" :style="{transform:'translateY('+topPadding+'px)'}">
<div class="table-responsive td_content" :style="{height: viewHeight + 'px'}" ref="td_content">
<table class="table table-bordered table_inner" ref="table">
<thead style="display: none;">
<tr>
<th>Log</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, tr_i) in rows" :key="tr_i">
<td class="ellipsis" :style="{height: childHeight + 'px'}">
<span>{{row['value']}}</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>`,
props: ["items", "viewHeight", "childHeight"],
data() {
return {
scrollHeight: 0,
topPadding: 0,
rows: []
};
},
methods: {
// Below is the first bit of problem code
calculateItems() {
let start = (
this.$refs.td_scroll.scrollTop /
this.$refs.td_scroll.scrollHeight *
this.items.length
);
let viewHeight = this.$refs.td_scroll.getBoundingClientRect().height + 2;
let end = (start) + (viewHeight / this.childHeight);
this.scrollHeight =
this.childHeight * this.items.length + this.childHeight;
this.topPadding = this.childHeight * (start);
//start = Math.round(start);
//end = Math.trunc(end);
this.rows = this.items.slice(start, end);
start = start;
end = end;
this.$emit("change", {
start,
end
});
},
// End problem code 1
refresh() {
window.requestAnimationFrame(this.calculateItems);
}
},
computed: {
// Below is the second bit of problem code
td_scrollHeight() {
let styles = {};
if (this.items.length > 6) {
styles.height = this.viewHeight - 50 + 10 + "px";
}
return styles;
}
// End problem code
},
watch: {
items() {
this.refresh();
}
},
mounted() {
this.refresh();
}
};
new Vue({
el: '#app',
data: {
logStart: 0,
logEnd: 0,
logs: [{"value": "0"}, {"value": "1"}, {"value": "2"}, {"value": "3"}, {"value": "4"}, {"value": "5"}, {"value": "6"}, {"value": "7"}, {"value": "8"}, {"value": "9"}, {"value": "10"}, {"value": "11"}, {"value": "12"}, {"value": "13"}, {"value": "14"}, {"value": "15"}, {"value": "16"}, {"value": "17"}, {"value": "18"}, {"value": "19"}, {"value": "20"}, {"value": "21"}, {"value": "22"}, {"value": "23"}, {"value": "24"}, {"value": "25"}, {"value": "26"}, {"value": "27"}, {"value": "28"}, {"value": "29"}, {"value": "30"}, {"value": "31"}, {"value": "32"}, {"value": "33"}, {"value": "34"}, {"value": "35"}, {"value": "36"}, {"value": "37"}, {"value": "38"}, {"value": "39"}, {"value": "40"}, {"value": "41"}, {"value": "42"}, {"value": "43"}, {"value": "44"}, {"value": "45"}, {"value": "46"}]
},
methods: {
updateShowing(pos) {
this.logStart = pos.start;
this.logEnd = pos.end;
}
},
components: {
comp
}
})
.table-responsive {
width: 100.1%;
}
.table-responsive.container_table {
overflow: hidden;
}
.table-responsive>.table-bordered {
border: 0;
border-collapse: collapse;
box-sizing: content-box;
width: 100.1%;
max-width: 100.1%;
}
.table_outer {
padding: 0;
width: 100.1%;
}
.table_outer>thead>tr>th {
position: relative;
}
.table_outer>tbody>tr>td {
padding: 0;
border: none;
}
.table_outer .td_scroll {
overflow: hidden;
overflow-y: overlay;
position: relative;
display: block;
}
.table_outer .td_content {
overflow: hidden;
}
.table_outer .table_inner {
margin-bottom: 0px;
}
.host {
overflow: hidden;
overflow-y: auto;
position: relative;
}
.scrollable-content {
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
}
.total-padding {
width: 1px;
opacity: 0;
}
.table-bordered thead td,
.table-bordered thead th {
border: 0;
}
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
{{logs.length}} Log{{(logs.length > 1 || logs.length === 0 || !logs) ? 's' : ''}}
<span v-show="logEnd !== 0">
<span> | Showing: {{logStart}} - {{logEnd}}</span>
</span>
<comp :items="logs" :view-height="350" :child-height="50" @change="updateShowing($event)"></comp>
</div>
Вот код блока, о котором идет речь, в приведенной выше демонстрации:
calculateItems() {
let start = (
this.$refs.td_scroll.scrollTop /
this.$refs.td_scroll.scrollHeight *
this.items.length
);
let viewHeight = this.$refs.td_scroll.getBoundingClientRect().height + 2;
let end = (start) + (viewHeight / this.childHeight);
this.scrollHeight =
this.childHeight * this.items.length + this.childHeight;
this.topPadding = this.childHeight * (start);
//start = Math.round(start);
//end = Math.trunc(end);
this.rows = this.items.slice(start, end);
start = start;
end = end;
this.$emit("change", {
start,
end
});
},
Я пробовал различные методы округления, а также регулярные сложения и вычитания, и, конечно, проверки условий, чтобы получить числа, где я их хочу. Ничего из этого не сработало.
Я также надеялся, что прокрутка будет плавной, но если это невозможно, то ладно.
В самом коде присутствует дополнительная HTML-разметка, которая не влияет на вычисление диапазона, поэтому я не включил ее в демонстрационную версию. HTML-разметка должна оставаться такой, как есть, если это возможно.
Есть ли способ правильно рассчитать диапазон видимых предметов? (1)
1: я не ищу библиотеку, чтобы сделать это для меня.