虚拟列表瀑布流-史上最强仿小红书思路详解及避坑小贴士

总体思路

经过一系列操作用 <math xmlns="http://www.w3.org/1998/Math/MathML"> "计算属性" \color{red}{"计算属性"} </math>"计算属性"算出来,得到渲染数据列表renderList,在模版中用v-for将数据渲染出来

所需要数据

  • 图片渲染数据列表renderList[],里面包含位置信息(top,left);
  • 原始数据imageList[] ;
  • 当前item宽度itemWidth ;
  • 高度列表heightsArray[] ,这里记录着每一列的高度

正文

理解数据是怎么排列的:初始高度列表为[0,0,0,0]这里列表的项数取决于你屏幕的宽度,你想要他是几列(这里你可以根据屏幕的宽度来设置)瀑布流就是几列。

1.高度列表heightsArray[]的计算

原始数据imageList里面第一条数据计算出他的高度(这里讨论数据里面有图片的高度信息),再计算出高度列表里面的最小值的index,更新这个位置上的值(就是原值加上间距),那么此时列表就会变成 [ 200 , 0 , 0 , 0 ]重复上述步骤,最后的这个高度列表heightsArray[]里面的最大值就会是外层container的高度。

获取当前每一项item的宽度:

js 复制代码
/**
 *
 * @param {*} containerWidth 容器宽度
 * @param {*} colomn 列数
 * @param {*} spacing 列间距与行间距相等
 * @returns itemwidth 每列的宽度
 */
export function getColumns(containerWidth, colomn, spacing) {
  const itemwidth =Math.floor((containerWidth-(colomn+1) * spacing)/colomn) 
  return itemwidth;
}

2.item位置坐标(left top)

为每一个item添加left,top值

js 复制代码
/**
 * 获取容器的高度以及每一张图片的left  top值 坐标值
 * !!注意: 图片的坐标值是相对于容器的坐标值
 * @param {*} imageList 图片列表[{url:'',width},{}]
 * @param imageWidth  图片宽度固定的  按照容器宽度  每列多少来确定
 */

export function getContainerHeightAndImageCoordinate(
  imageList,
  heightsArray,
  spacing,
  imageWidth
) {
  // 循环遍历图片更新图片的坐标值  top  left
  imageList.forEach((item, index) => {
    const minHeightIndex = getMinHeightIndex(heightsArray);
    // 更新图片的坐标值
    item.left = minHeightIndex * imageWidth + (minHeightIndex + 1) * spacing;
    item.top = heightsArray[minHeightIndex];
    // 更新高度列表
    // (实际宽度 / 图片宽度) * 图片高度 
    heightsArray[minHeightIndex] +=(imageWidth/item.width)*item.height+ spacing;
  });
  return { imageList1:imageList, heightsArray };
}


// 获取最小列的index
/**
 *
 * @param {*} heights 高度列表Array
 * @returns index 返回最小高度的index
 */
export function getMinHeightIndex(heightArray) {
  const index = heightArray.findIndex(
    (item) => item === Math.min(...heightArray)
  );
  return index;
}
// 获取最大列的最大高度
/**
 *
 * @param {*} heights 高度列表Array
 * @returns index 返回最小高度的index
 */
export function getMaxHeight(heightArray) {
  const index = heightArray.findIndex(
    (item) => item === Math.max(...heightArray)
  );
  return heightArray[index];
}

3.监听原始数据的变化添加位置坐标以及索引值

监听原始数据的变化添加位置坐标以及索引值,同时更新containerheight

js 复制代码
// 监测props的变化  传入原始数据  部分改变
watch(
  () => props.data,
  () => {
    // 获取当前itemwidth
    itemWidth.value = getColumns(
      containerwidth(),
      colomns.value,
      props.rowSpace
    );
    // 获取加了坐标的数据  以及更新的高度列表
    const { imageList1, heightsArray } = getContainerHeightAndImageCoordinate(
      props.data,
      heightsList.value,
      props.rowSpace,
      itemWidth.value
    );
    // 获取高度列表
    heightsList.value = heightsArray;
    // 获取包含坐标的图像数据列表
    newImagelist.value = imageList1;
    // 最高高度 这里
    containerheight.value = getMaxHeight(heightsList.value);
    // 为每一个渲染数据排序
    let startlength = 0;
    if (image.value.length <= 19) {
      props.data.map((item, index) => {
        item.idx = index;
        image.value.push(item);
      });
    } else {
      startlength = image.value.length;
      props.data.map((item, index) => {
        item.idx = startlength + index;
        image.value.push(item);
      });
    }
    getrenderList();
  }
);

4.确定对于原始数据需要截取的startindex,endindex的区间item

确定原始startindex,endindex,初始值可以是你获取第一页数据的个数,比如你第一页展示的图片数据20个,那么就可以设置startindex,endindex为0,19。

根据我们滑动的距离动态的改变startindex,endindex的值 这里会用到上面为每一项图片添加的索引值idx.

这里是核心代码 startindex计算逻辑是取----只要正在滑动过程完结后,当此时有一个item的bottom与上刻线相交那么这个item的idx就是startindex,下刻线同理。上下刻线就是取当前视口高度的一倍,避免在用户缓慢滑动时出现空白。

js 复制代码
// 获取上下index  核心代码
/**
 * 
 * @param {*} imageList  当前所有图片列表  从你打开页面开始,就一直的增加的图片列表这个列表会随你滑动页面不断push数据进去
 * @param {*} scrollTop  这个是当前页面滑动了多少的值
 * @param {*} itemwidth 当前屏幕宽度下计算出来的itemwidth
 * @returns 
 */
export function getIndex(imageList,scrollTop,itemwidth) {
    // 获取当前视口高度
    const containerWidth = window.innerHeight
    let startindex
    let endindex
    for (let i = 0; i < imageList.length; i++) {
      const itemheight = (itemwidth*imageList[i].height) / imageList[i].width;
      if (scrollTop-imageList[i].top-itemheight<containerWidth) {
        startindex =  imageList[i].idx
        break; 
      }
    }
    for (let i = 0; i < imageList.length; i++) {
      const itemheight = (itemwidth*imageList[i].height) / imageList[i].width;
      if (imageList[i].top-scrollTop-2*containerWidth>0) {
        endindex =  imageList[i].idx
        break; 
      }else{
        endindex =  imageList.length-1
      }
    }
    if(endindex<19){
      endindex = 19
    }
    return{
      startindex,endindex
    }
}

5.根据startindex,endindex截取image

render1就是我们最终得到的渲染数据,使用v-for渲染数据即可,在v-for数据上添加style

js 复制代码
const render1 = computed(() => {
  return image.value.slice(s1.value, en1.value);
});

总结

逻辑推导:

1.需要有一个渲染列表,这个列表包含元素需要在哪里展示的定位坐标,left,top

2.坐标值怎么来呢?循环遍历每一项,根据每一项的高度累加到一个高度列表中得到left,top。

3.滑动屏幕时怎么动态展示我们想要的某一部分item

4.就需要动态改变startindex,endindex,对元数据进行截取操作赋值给渲染列表。

这里赋值需要用计算属性计算出来不能直接赋值,直接赋值会有闪烁!!!切记,不知道什么原因,有知道的道友望告知!!!!

此致!

相关推荐
T^T尚3 小时前
uniapp H5上传图片前压缩
前端·javascript·uni-app
出逃日志4 小时前
JS的DOM操作和事件监听综合练习 (具备三种功能的轮播图案例)
开发语言·前端·javascript
XIE3924 小时前
如何开发一个脚手架
前端·javascript·git·npm·node.js·github
山猪打不过家猪4 小时前
React(五)——useContecxt/Reducer/useCallback/useRef/React.memo/useMemo
前端·javascript·react.js
前端青山4 小时前
React事件处理机制详解
开发语言·前端·javascript·react.js
科技D人生4 小时前
Vue.js 学习总结(14)—— Vue3 为什么推荐使用 ref 而不是 reactive
前端·vue.js·vue ref·vue ref 响应式·vue reactive
对卦卦上心4 小时前
React-useEffect的使用
前端·javascript·react.js
练习两年半的工程师4 小时前
React的基本知识:事件监听器、Props和State的区分、改变state的方法、使用回调函数改变state、使用三元运算符改变state
前端·javascript·react.js
啵咿傲4 小时前
在React中实践一些软件设计思想 ✅
前端·react.js·前端框架
GIS好难学5 小时前
《Vue零基础入门教程》第二课:搭建开发环境
前端·javascript·vue.js·ecmascript·gis·web