ноль, введение
В этой статье в основном представлена реализация каскадной компоновки страниц веб-сайта, в том числе:
- Что такое водопад
- Принцип реализации водопадного потока
- Сценарии использования водопада
- Какие проблемы в реализации и как их решить
- Расширяемые сценарии использования
1. Что такое водопад
Поток водопада, также известный как макет потока водопада, является популярным макетом страницы веб-сайта. Визуальное представление представляет собой неровный многоколоночный макет элементов одинаковой ширины и высоты, и по мере прокрутки страницы новые элементы добавляются к самому короткому столбцу и загружаются вниз.
Во-вторых, принцип реализации водопадного потока
Поток водопада, по сути, состоит в том, чтобы найти столбец с наименьшей высотой среди столбцов и добавить новые элементы в конец столбца.Пока есть новые элементы, которые нужно расположить, продолжайте находить столбец с наименьшей высотой в столбце. все столбцы, а последующие элементы помещаются в столбец с наименьшей высотой.
Графический базовый поток водопада
Давайте посмотрим, почему мы всегда ищем самый маленький столбец?
Посмотрите на порядок расположения на рисунке 1. Верх первого ряда элементов будет на одной высоте, и они будут располагаться вверху по очереди.После того, как первый ряд будет заполнен, второй ряд будет располагаться от слева направо. Однако при таком расположении одна из колонок слишком длинная или одна из колонок слишком короткая.
Чтобы решить проблему, что столбец на рисунке 1 может быть слишком длинным или слишком коротким, мы организуем элементы в кратчайшей колонке в соответствии с методом на рисунке 2.
В-третьих, использование сцены водопада
Когда поток водопада скользит, будут появляться новые вещи, привлекая вас к продолжению исследования вниз и умному использованию визуального уровня и произвольного потока зрения для снятия зрительной усталости.Использование этого решения может продлить визуальную остановку пользователя и улучшить зрение пользователя. Viscosity подходит для случайного просмотра без цели, например, для покупок, просмотра во время прогулки, поэтому он больше подходит для изображений, товаров и информационных сцен. Многие веб-сайты, связанные с электронной коммерцией, используют водопады для их переноса.
Эффект водопада Mogujie для ПК на изображении выше представляет собой расширение и преобразование, основанное на базовом водопаде, а другой контент, не относящийся к водопаду, вставляется в один или несколько столбцов в верхней части водопада.
В этой статье будет представлен сценарий реализации этого расширенного каскадного потока с четырьмя столбцами. Применимыми базовыми сценариями являются следующие:
4. Какие проблемы в реализации водопадного течения и как их решить
- Как вставить контент без водопада?
- Как найти наименьшую высоту всех столбцов?
- Как визуализировать поток водопада?
Vue реализует водопадный поток
Мы используем фреймворк Vue для реализации каскадного потока, и некоторые его собственные свойства упрощают реализацию водопадного потока.
- Очень удобно через ref получать высоту каждого столбца, а через алгоритм сравнения вычислять столбец с наименьшей высотой.
- Получив столбец с наименьшей высотой, поместите данные следующего элемента, которые нужно вставить, в список данных (columnList) самого маленького столбца и завершите визуализацию элемента, манипулируя данными.
- Используйте именованные слоты Vue, чтобы вставить другой контент, не относящийся к водопаду, поверх водопада.
- Отслеживайте визуализацию элемента с помощью часов, чтобы определить, следует ли продолжать визуализацию и запрашивать дополнительные данные элемента.
Как вставить контент без водопада
Передавайте элементы, не являющиеся водопадом, в качестве содержимого родительского компонента в дочерние компоненты водопада через именованные слоты Vue.
- Родительский компонент связывает именованный слот с атрибутом слота в шаблоне HTML, а содержимое, не связанное с водопадом, предоставляется дочернему компоненту как содержимое именованного слота.
- Именованные слоты: первый столбец, второй столбец, третий столбец, последний столбец, слот слияния, которые представляют первый, второй, третий, четвертый столбцы и столбцы слияния соответственно.
- Подкомпонент использует имя слота, чтобы определить, в какой столбец поместить некаскадный контент. Если слот существует, вставьте его содержимое в верхнюю позицию.
- Из-за специфики объединенного столбца, если объединенный столбец включен, объединенный столбец позиционируется абсолютно вверх, а столбец, соответствующий водопадному потоку, учитываемому объединенным столбцом, перемещается вниз. Родительский компонент передает параметры, связанные со столбцом слияния, в дочерний компонент: слияние (определение того, включен ли столбец слияния), mergeHeight (высота столбца слияния), mergeColumms (какие 2 столбца объединены).
Код
<!-- 父组件 -->
<div class="parent">
<Waterfall :merge=true :mergeHeight=800 mergeColumns=[2,3]>
<template slot="first-col">
<!-- 第一列内容... -->
</template>
<template slot="second-col">
<!-- 第二列内容... -->
</template>
<template slot="third-col">
<!-- 第三列内容... -->
</template>
<template slot="last-col">
<!-- 第四列内容... -->
</template>
<template slot="merge-col">
<!-- 合并内容... -->
</template>
</Waterfall>
</div>
<!-- 子组件(waterfall) -->
<div class="child">
<!-- 第一列 -->
<div ref="column1" :style="{marginTop: merge && mergeColumns.indexOf(1) > -1 ? mergeHeight + 'px':''}">
<template v-if="$slots['first-col']">
<slot name="first-col"></slot>
</template>
<template v-for="(item, index) in columnList1">
<!-- 第一列瀑布流内容... -->
</template>
</div>
<!-- 第二列 -->
<div ref="column2" :style="{marginTop: merge && mergeColumns.indexOf(2) > -1 ? mergeHeight + 'px':''}">
<template v-if="$slots['second-col']">
<slot name="second-col"></slot>
</template>
<template v-for="(item, index) in columnList2">
<!-- 第二列瀑布流内容... -->
</template>
</div>
<!-- 第三列 -->
<div ref="column3" :style="{marginTop: merge && mergeColumns.indexOf(3) > -1 ? mergeHeight + 'px':''}">
<template v-if="$slots['third-col']">
<slot name="third-col"></slot>
</template>
<template v-for="(item, index) in columnList3">
<!-- 第三列瀑布流内容... -->
</template>
</div>
<!-- 第四列 -->
<div ref="column4" v-if="is4Columns">
<template v-if="$slots['last-col']">
<slot name="last-col"></slot>
</template>
<template v-for="(item, index) in columnList4">
<!-- 第四列瀑布流内容... -->
</template>
</div>
<!-- 合并块非瀑布流内容 -->
<div class="column-merge" v-if="merge" :style="{left: (mergeColumns[0] - 1)*330 + 'px'}">
<slot name="merge-col"></slot>
</div>
</div>
Как найти наименьшую высоту всех столбцов
Каждый столбец определяет ref, а высота текущего столбца получается через ref.Если над столбцом есть объединенный блок, то высота должна быть добавлена к высоте объединенного блока, а затем к высоте 4-х столбцов сравнивается, чтобы получить минимальную высоту, а затем вычисляется минимальная высота соответствующего количества столбцов.
Код
// 通过ref获取每列高度,column1,column2,column3,column4分别代表第一、二、三、四列
let columsHeight = [this.$refs.column1.offsetHeight, this.$refs.column2.offsetHeight, this.$refs.column3.offsetHeight, this.$refs.column4.offsetHeight]
// 如果包含合并块, 则更新高度,合并块下的列高要增加合并块的高度
if(this.merge){
// 如果有合并列,则合并列下的列高度要加合并内容的高度。
columsHeight[0] = this.mergeColumns.indexOf(1) > -1 ? columsHeight[0] + this.mergeHeight : columsHeight[0];
columsHeight[1] = this.mergeColumns.indexOf(2) > -1 ? columsHeight[1] + this.mergeHeight : columsHeight[1];
columsHeight[2] = this.mergeColumns.indexOf(3) > -1 ? columsHeight[2] + this.mergeHeight : columsHeight[2];
columsHeight[3] = this.mergeColumns.indexOf(4) > -1 ? columsHeight[3] + this.mergeHeight : columsHeight[3];
}
// 获取各列最小高度
let minHeight = Math.min.apply(null, columsHeight);
// 通过最小高度,得到第几列高度最小
this.getMinhIndex(columsHeight, minHeight).then(minIndex => {
// 渲染加载逻辑
});
// 获取高度最小索引函数
getMinhIndex(arr, value){
return new Promise((reslove) => {
let minIndex = 0;
for(let i in arr){
if(arr[i] == value){
minIndex = i;
reslove(minIndex);
}
}
});
}
Как визуализировать водопад
Поток водопада часто используется при бесконечной выпадающей загрузке или в сценариях, когда загружается большой объем данных и содержится много элементов изображения, поэтому обычно невозможно получить все данные за один раз, и он не будет отображать все полученные данные на страницу за один раз. В противном случае легко привести к зависанию страницы и повлиять на взаимодействие с пользователем, поэтому очень важно, когда отображать и когда продолжать запрашивать данные.
когда рендерить
Выберите область рендеринга как высоту прокрутки + 1,5-кратную высоту видимой области, что может не только предотвратить прокрутку пользователя вниз с белого экрана, но и предотвратить влияние слишком большого рендеринга на работу пользователя. еслиМинимальная высота столбца — высота прокрутки , элемент будет продолжать отображаться, в противном случае он не будет отображаться.
Когда запрашивать данные
Когда количество отображаемых элементов + расчетное количество элементов, которые могут отображаться в видимой области > количество запрошенных элементов, продолжайте запрашивать больше данных, чтобы предотвратить напрасные запросы. еслиКоличество загруженных элементов + предполагаемое количество элементов, которые можно отобразить на одном экране > количество элементов, полученных по всем запросам , затем инициируйте следующий запрос, чтобы получить больше данных.
Основные идеи рендеринга потока водопада
- Отслеживайте прокрутку, определяйте, выполняются ли условия рендеринга, и запускайте рендеринг, если условия выполняются.
- Определите индекс рендеринга renderIndex, renderIndex + 1 после рендеринга элемента, отслеживайте изменение renderIndex в реальном времени и определяйте, соответствует ли он условиям рендеринга и запроса данных.
- После получения минимального индекса столбца высоты вставьте следующий элемент в столбец и активируйте renderIndex + 1 для следующего раунда оценки рендеринга.
Код
data() {
return {
columnList1: [], // 第一列元素列表
columnList2: [],
columnList3: [],
columnList4: [],
renderIndex: -1, // 渲染第几个item
isRendering: false, // 是否正在渲染
itemList: [], // 所有元素列表
isEnd: false
};
}
watch: {
renderIndex(value) {
// 当前滚动条高度
const scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
// 最小列高度 - 滚动高度 < 可视区域高的的1.5倍
if (renderMinTop - scrollTop < winHeight * 1.5) {
this.renderWaterfall();
}
// 已加载的元素个数 + 一屏可以展示元素预估个数 > 所有请求拿到的元素个数
if (loadedItemNum + canShowItemNum > this.itemList.length && !this._requesting && !this.isEnd) {
// 请求瀑布流数据
this.getData();
}
}
}
scroll() {
// 当前滚动条高度
const scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
// 底部检测高度
const bottomDetectionTop = this.$refs.bottomDetection.offsetTop;
const tempLastScrollTop = lastScrollTop; // lastScrollTop:上次一滚动高度
lastScrollTop = scrollTop;
if (tempLastScrollTop === -1) {
this.renderWaterfall();
}
// 如果是向下滚动则判断是否需要继续渲染
if (scrollTop > tempLastScrollTop) {
if (bottomDetectionTop - tempLastScrollTop < winHeight * 1.5 && !this.isRendering) {
this.renderWaterfall();
}
}
}
renderWaterfall() {
// 如果还没有数据、所有数据已经渲染完成、正在渲染则不进行渲染计算操作
if (this.itemList.length === 0 || this.renderIndex >= this.itemList.length - 1 || this.isRendering) {
if (this.renderIndex === this.feedList.length - 1 && !this._requesting && !this.isEnd) {
this.getData();
}
return;
}
this.isRendering = true;
/***
*** 获取最小高度代码
***/
this.getMinhIndex(columnsHeight, minHeight).then(minIndex => {
const key = `columnList${minIndex + 1}`;
let itemData = this.itemList[this.renderIndex + 1];
this[key] = this[key].concat(itemData);
this.$nextTick(() => {
this.renderIndex = this.renderIndex + 1;
this.isRendering = false;
});
});
}
5. Расширяемые сценарии использования
Для гибкого использования потока водопада он готов к расширению во время проектирования.Из кода HTML-шаблона видно, что содержимое именованного слота может быть размещено в любой колонке, и ограничений нет, поэтому его можно распространить на следующие сценарии.