Кроме этих, еще попробовал другие варианты, в итоге что то получилось.
Работающие варианты:
Эти не работают:
- this.$f7.virtualList.get().clearCache();
- this.$forceUpdate(); //не работает
Во всех случаях, после обновления есть мерцание, а на телефоне(Android, средний Xiaomi-RedmiNote8Pro) когда быстро прокручиваешь, navbar остается на месте(и цвет не меняется), а где content чернота, иногда видна главная страница.
Вопросы:
- Какой из выше указанных(работающих) методов более правильный?
- Может еще предложите другие варианты?
- Может для виртуального листа сделать новый метод обновления высоты?
Почему это важно:
- У меня есть списки документов, где есть куча атрибутов(доходят до 15шт.), которые нужно показать и они всегда меняются, у кого что. Не возможно предугадать заранее высоту элемента списка. В данный момент решаю, показывают в виде таблицы.
Внизу сделал пример, где есть код. Через правый верхний угол можно(нужно выбрать) метод повторного рендеринга.
Код:
<template>
<f7-page name="virtual-list"
:page-content="false">
<f7-navbar>
<f7-nav-title>
F7 Virtual List <span :class="processColor">[{{ renderType || 'none'}}]</span>
<span class="subtitle">
Dynamic Height
[
key:{{ f7ListKey }}
/
items:{{ items.length }}
]
</span>
</f7-nav-title>
<f7-nav-right>
<!-- <f7-link @click="ScrollStop" v-if="scrolling">-->
<!-- [scrollStop]-->
<!-- </f7-link>-->
<!-- <f7-link @click="ScrollStart" v-else>-->
<!-- [scrollStart]-->
<!-- </f7-link>-->
<f7-link @click="$refs.settings.open()">
<f7-icon f7="gear_alt"/>
<br/>
<div style="font-size: 10px; transform: rotate(-90deg);">
v4
</div>
</f7-link>
</f7-nav-right>
</f7-navbar>
<f7-sheet ref="settings"
style="height:auto; --f7-sheet-bg-color: #fff;"
backdrop>
<f7-block-title medium class="margin-top">
Rendering method:
</f7-block-title>
<f7-list>
<f7-list-item radio title="None"
name="type"
:checked="renderType===null"
@change="Type_change(null)"/>
<f7-list-item radio title="f7.virtualList.clearCache()"
name="type"
:checked="renderType==='VlClearCache'"
@change="Type_change('VlClearCache')"/>
<f7-list-item radio title="f7.virtualList.update()"
name="type"
:checked="renderType==='VlUpdate'"
@change="Type_change('VlUpdate')"/>
<f7-list-item radio title="f7.virtualList.replaceAllItems()"
name="type"
:checked="renderType==='VlReplaceAllItems'"
@change="Type_change('VlReplaceAllItems')"/>
<f7-list-item radio title="Vue.$forceUpdate()"
name="type"
:checked="renderType==='VueForceUpdate'"
@change="Type_change('VueForceUpdate')"/>
<f7-list-item radio title="Vue.key"
name="type"
:checked="renderType==='VueKey'"
@change="Type_change('VueKey')"/>
</f7-list>
</f7-sheet>
<f7-page-content :infinite="infiniteScroll"
id="Page"
ref="F7List"
:infinite-distance="infiniteDistance"
:infinite-preloader="false"
@infinite="onInfinite">
<f7-block v-if="visible && !items.length">
Loading..
</f7-block>
<f7-list v-else-if="visible && items.length"
virtual-list
:virtual-list-params="f7VirtualListParams"
:key="f7ListKey"
class="no-margin-vertical"
media-list
no-hairlines
no-hairlines-between
no-chevron>
<ul>
<f7-list-item
v-for="(item, index) in vlData.items"
:key="index"
media-item
link="#"
:data-id="item.id"
ref="List"
class="card"
:title="item.title"
:subtitle="item.subtitle"
:text="item.text"
:after="(heights[item.id] || '~')+'px'"
:style="`top: ${vlData.topPosition}px`">
<div slot="media" style="width: 80px;">
<img :src="item.image"
style="min-width: 80px !important; height: 80px !important;">
</div>
<div slot="root-end" v-if="item.footer" class="padding-horizontal">
{{ item.footer }}
</div>
</f7-list-item>
</ul>
</f7-list>
<f7-block v-else>
Reloading..
</f7-block>
</f7-page-content>
</f7-page>
</template>
<script>
const SomeText = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`
const ImageApi = 'https://i.picsum.photos/id/';
const SomeImages = [1022, 103, 1039, 1043, 1041, 1062, 1069, 110, 161, 164];
const SomeColors = ['blue', 'red', 'orange', 'green', 'pink', 'lime'];
//number of items
const ItemsMax = 1000;
const ItemsPerRequest = 30;
let checker = 0;
function randomNumberBetween($Min, $Max) {
return Math.floor(Math.random() * $Max) + $Min;
}
export default {
data() {
return {
processColor: '',
visible: true,
renderType: null,
f7ListKey: 0,
heights: {},
start: 1,
items: [],
vlData: {},
infiniteScroll: true,
infiniteAllow: true,
infiniteDistance: 600,
scrolling: false
};
},
computed: {
f7VirtualListParams() {
return {
items: this.items,
rowsBefore: 4,
rowsAfter: 4,
renderExternal: this.renderExternal,
height: this.Height,
};
},
},
methods: {
fakeApi($Start) {
// console.warn('fakeApi:', $Start)
let id = $Start;
setTimeout(() => {
for (let i = 1; i <= ItemsPerRequest; i++) {
let randomTitle = SomeText.slice(randomNumberBetween(20, 60), randomNumberBetween(61, 200));
let randomText = SomeText.slice(0, randomNumberBetween(0, 100));
this.items.push({
id: id,
title: id + ' ' + randomTitle,
subtitle: (randomNumberBetween(1, 100) % 2) ? `Subtitle ${id}` : null,
text: randomText,
image: this.Image(id),
color: this.Color(id),
footer: (randomNumberBetween(1, 100) % 3 === 1) ? `:-)` : null,
height: 73
});
++id;
}
this.start = id;
this.f7ListKey += 1;
this.infiniteAllow = true;
}, randomNumberBetween(10, 300));
},
Image($ID) {
const PixelRatio = this.$f7.device.pixelRatio || 1;
const Width = 80 * PixelRatio;
const Height = 80 * PixelRatio;
return ImageApi + SomeImages[$ID % 10] + '/' + Width + '/' + Height + '.jpg'
},
Color($ID) {
return SomeColors[$ID % 6];
},
Height($Item) {
return this.heights[$Item.id] || 73;
},
renderExternal(vl, vlData) {
this.vlData = vlData;
this.updateVl();
},
ProcessColor() {
this.processColor = 'text-color-yellow';
setTimeout(() => {
this.processColor = '';
}, 500);
},
updateVl() {
console.warn('UPDATEVL')
++checker;
if (this.$refs.List) {
this.ProcessColor();
for (let Index in this.$refs.List) {
let item = this.$refs.List[Index];
let {
dataset: {id}, //id of item
clientHeight, //height without scrollBar, border, and the margin.
offsetHeight //height wihtout margin
} = item.$el;
let {marginTop, marginBottom} = window.getComputedStyle(item.$el); //in pixels
//calculating on fly,
const MarginTop = parseInt(marginTop) / 2;
const MarginBottom = parseInt(marginBottom) / 2;
// console.warn('clientHeight:', clientHeight,
// 'offsetHeight:', offsetHeight,
// 'marginTop:', marginTop,
// 'marginBottom:', marginBottom)
this.heights[id] = clientHeight + MarginTop + MarginBottom;
}
switch (this.renderType) {
case 'VlClearCache':
this.$nextTick(() => {
if (checker % 2 === 0) {
let timeStart = +new Date();
this.$f7.virtualList.get().clearCache();
console.log('UpdateVl VlClearCache:', +new Date() - timeStart)
}
});
break;
case 'VlUpdate':
this.$nextTick(() => {
if (checker % 2 === 0) {
let timeStart = +new Date();
this.$f7.virtualList.get().update();
console.log('UpdateVl VlUpdate:', +new Date() - timeStart)
}
});
break;
case 'VlReplaceAllItems':
this.$nextTick(() => {
if (checker % 2 === 0) {
let timeStart = +new Date();
this.$f7.virtualList.get().replaceAllItems(this.items);
console.log('UpdateVl VlReplaceAllItems:', +new Date() - timeStart)
}
});
break;
case 'VueForceUpdate':
let timeStart = +new Date();
this.$forceUpdate();
console.log('UpdateVl VueForceUpdate:', +new Date() - timeStart)
break;
case 'VueKey':
if (checker % 2 === 0) {
this.f7ListKey += 1;
console.log('UpdateVl VueKey')
}
break;
default:
break;
}
}
},
Type_change($Type) {
this.visible = false;
this.renderType = $Type;
setTimeout(() => {
this.start = 1;
this.items = [];
this.vlData = {};
this.heights = {};
this.f7ListKey = 0;
this.visible = true;
this.fakeApi(this.start);
}, 500)
},
onInfinite() {
console.warn('onInfinite()')
if (this.infiniteAllow) {
this.infiniteAllow = false;
this.fakeApi(this.start);
}
},
ScrollStart($Event, $Height = 0) {
if (!this.scrolling) {
this.scrolling = true;
let listContainer = this.$$("#Page")
// console.log('LC:', listContainer.scrollHeight)
// console.log('LC:', listContainer.scrollTop())
// this.scrolling = setInterval(() => {
$Height += 400;
listContainer.scrollTop($Height, 5000, () => {
this.scrolling = false;
this.ScrollStart(null, $Height);
});
// console.log('scrolling:', $Height)
// }, 8000);
}
},
ScrollStop() {
// clearInterval(this.scrolling);
this.scrolling = true;
}
},
mounted() {
this.$nextTick(() => {
this.updateVl('nextTick');
});
},
created() {
this.fakeApi(this.start);
}
};
</script>
<style scoped>
.item-media {
min-width: 80px !important;
}
</style>