你的位置:自慰女孩 > 性爱大师第一季 >

    
发布日期:2024-11-29 20:28    点击次数:55

91porn,com 原生JS手写瀑布流布局

我为什么要手写瀑布流布局,是因为行将要作念一个h5商城,首页商品是瀑布流布局的,还从来没写过呢,毕竟用的简直是太少了。固然有许多现成的,但行为一个有追求的风景员91porn,com,如故要在时代限度内尽量搞懂其收场旨趣。

什么是瀑布流布局❝

瀑布流,又称瀑布流式布局。是相比流行的一种网站页面布局,视觉进展为散乱不都的多栏布局,跟着页面升沉条向下升沉,这种布局还会不绝加载数据块并附加至现时尾部。

我作念了个动画来确认这少量:

图片

这里为了看的明晰,咱们假定每个元素是按次渲染的,不错看到后续的每个元素都在找高度最小的那一列去摆设。这种布局十分适用于各个子元素高度不均匀的情况。

整期望路

参考了国内两大着名网站抖音pc版和小红书pc版,发现一个共同点,他们都是无论在多大的屏幕尺寸下,「扫数元素的宽度是一样的」,即使拖动更动了屏幕大小,元素宽度动态调治了,每个元素跟其他兄弟元素的宽度亦然一致的,「只消高度不一致」,而这等于程序的瀑布流布局。

图片

抖音

图片

小红书

那么,我就不错暂时先把每个元素的宽度定死,先不有计划更动屏幕宽度的情况。

<!DOCTYPE html><html lang='en'><head>    <meta charset='UTF-8'>    <meta name='viewport' content='width=device-width, initial-scale=1.0'>    <title>瀑布流布局demo</title>    <link rel='stylesheet' href='styles.css'></head><body>  <div id='container'>    <!-- 履行块 -->    <div class='item'>1</div>    <div class='item'>2</div>    <div class='item'>3</div>    <div class='item'>4</div>    <div class='item'>5</div>    <div class='item'>6</div>    <div class='item'>7</div>    <div class='item'>8</div>    <div class='item'>9</div>    <div class='item'>10</div>    <div class='item'>11</div>    <div class='item'>12</div>    <div class='item'>13</div>    <div class='item'>14</div>    <div class='item'>15</div>    <div class='item'>16</div>    <div class='item'>17</div>    <div class='item'>18</div>    <div class='item'>19</div>    <div class='item'>20</div>  </div></body></html>

系数20个div,目下还莫得履行,无法撑开高度,咱们就假定扫数奇数的高100px,偶数的高150px,每一个的宽度都是200px,然后先用flex布局(粗略无谓flex布局,把item设为inline-block),望望会是什么后果。

丝袜电影
/* styles.css */#container {  display: flex;  flex-wrap: wrap;}.item {  flex-shrink: 0;  width: 200px;  margin-bottom: 10px;  background: #ccc;  border: 1px solid #999;  box-sizing: border-box;  text-align: center;}/* 立时高度,用于演示 */.item:nth-child(odd) {  height: 100px;}.item:nth-child(even) {  height: 150px;}

图片

flex和inline-block后果差未几,这可不是咱们想要的。那何如才能让每个元素去找到它该去的场地呢?这时就要用到「css定位」了。

光有定位还不够,还得用「js计较」现存元素的高度,才知说念每个元素到底该去哪。

要领:91porn,com

父元素相对定位,每个子元素十足定位。js凭证屏幕宽度和每个元素的宽度,计较最多清楚若干列。拿到扫数dom元素,遍历并及时计较每一列中现存元素的高度,找到高度最小的那一列,把现时遍历到的这个元素十足定位到那一列,因为每个元素宽度是一样的,十足定位的left值等于列数*元素宽度,top值等于这个最小高度。

到这里想路就理清了。

代码收场

先改一下css,把flex去掉,改用定位,其他代码不变。

/* styles.css */#container {  /* display: flex;  flex-wrap: wrap; */  position: relative;}.item {  /* flex-shrink: 0; */  position: absolute;}

目下是这么的了,毕竟扫数元素都十足定位了,没建树left和top值,就会都在页面左上角。

图片

别急,底下来用js建树每个元素的left和top。

咱们不雅察抖音和小红书的布局,就不错看出,它们是一列列的摆设的,垂直方进取是凹凸对都的(毕竟每个子元素宽度是一样的),水普通向是散乱不都的(因为不同子元素高度不一样),是以咱们不错把这些子元素漫衍成几列。

Q: 那到底是几列呢?

A: 得到父容器宽度,除以元素宽度和罅隙的和,向下取整,等于最多不错清楚的列数。

const container = document.getElementById('container')const containerWidth = container.offsetWidth// 列数const columnCount = Math.floor(containerWidth / (200 + 10));

有列数还不够,瀑布流的中枢是「散乱不都纵横交错,每个元素找最矮的那一列」去清楚。是以还要知说念每一列的总高度。开动化每列高度的数组,用来保存每列的总高度,开动值为0。

let columnHeights = Array(columnCount).fill(0);

然后遍历子元素,每次找到高度最矮的那一列,这里就要用到高度数组了,现时元素十足定位的left值等于下标乘以宽度加隔断的和,top值等于最矮的那一列的总高度。然后更新那一列的高度,等于加上元素自己高度和隔断的高度。

荟萃上头的代码,整理完善一下:

// script.jswindow.onload = function () {  // 得到父元素dom  const container = document.getElementById('container');  // 列宽,也等于每个子元素  const columnWidth = 200;  // 隔断宽度  const columnGap = 10;  // 父元素自己宽度  const containerWidth = container.offsetWidth;  // 计较最多清楚若干列  const columnCount = Math.floor(containerWidth / (columnWidth + columnGap));  // 开动化每列高度的数组  let columnHeights = Array(columnCount).fill(0);  const items = Array.from(container.getElementsByClassName('item'))  items.forEach(item => {    // 找到高度最矮的那一列的下标    const minColumnIndex = columnHeights.indexOf(Math.min(...columnHeights));    // 现时元素的top值等于最矮的那一列的高度    const top = columnHeights[minColumnIndex];    // 现时元素的left值等于列宽乘以最矮的那一列的下标    const left = minColumnIndex * (columnWidth + columnGap);    item.style.top = `${top}px`;    item.style.left = `${left}px`;    // 及时更新高度(自己高度+隔断)    columnHeights[minColumnIndex] += item.offsetHeight + columnGap;  });};

此时后果如下:

图片

基本上如故收场了。加载更多

一般这种网站数据都许多,都是分页加载的,咱们不错监听页面升沉事件,当页面触底时,动态加载更多量据。

window.addEventListener('scroll', () => {  if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {    // 模拟加载更多量据,动态创建出10个item元素    const newItems = [];    for (let i = 0; i < 10; i++) {      const newItem = document.createElement('div');      newItem.className = 'item';      newItem.style.height = `${Math.floor(Math.random() * 100) + 100}px`;      container.appendChild(newItem);      newItems.push(newItem);    }    // 依然跟上头一样遍历    newItems.forEach(item => {      // 找到高度最矮的那一列的下标      const minColumnIndex = columnHeights.indexOf(Math.min(...columnHeights));      // 现时元素的top值等于最矮的那一列的高度      const top = columnHeights[minColumnIndex];      // 现时元素的left值等于列宽乘以最矮的那一列的下标      const left = minColumnIndex * (columnWidth + columnGap);      item.style.top = `${top}px`;      item.style.left = `${left}px`;      // 及时更新高度(自己高度+隔断)      columnHeights[minColumnIndex] += item.offsetHeight + columnGap;    });  }});

运行了一遍才发现,只消一动弹,还没触底呢就抑止的加载更多,尝试加防抖也不成,那一定是判断触底的代码有问题,调试了一下才发现,原本是document.body.offsetHeight一直是0,难怪呢会一直触发加载更多呢,导致这里又徜徉了时辰,不知说念是什么原因。

图片

其后检查元素才想起来,原本是咱们的子元素一都都是十足定位的,导致父元素高度塌陷了,是以body的高度也一直是0。我还一直在看js的问题,哎!

知说念了原因,惩处风景就肤浅了,在每次遍历完成item数组,更新高度数组的值之后,给父元素container建树一个高度,这个高度等于最高的那一列的高度:

container.style.height = `${Math.max(...columnHeights)}px`;

很彰着目下这个代码不够优雅,相易代码过多。

咱们把中枢的那段共同代码封装成一个函数,接纳要遍历的那些dom元素的数组行为参数。

// 布局function layoutItem(items) {  items.forEach(item => {    // 找到高度最矮的那一列的下标    const minColumnIndex = columnHeights.indexOf(Math.min(...columnHeights));    // 现时元素的top值等于最矮的那一列的高度    const top = columnHeights[minColumnIndex];    // 现时元素的left值等于列宽乘以最矮的那一列的下标    const left = minColumnIndex * (columnWidth + columnGap);    item.style.top = `${top}px`;    item.style.left = `${left}px`;    // 及时更新高度(自己高度+隔断)    columnHeights[minColumnIndex] += item.offsetHeight + columnGap;  });}

然后在最脱手的时候,和页面触底的时候诀别调用这个layoutItems函数。

const initItems = Array.from(container.getElementsByClassName('item'));layoutItems(initItems);window.addEventListener('scroll', () => {  if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {    const newItems = [];    for (let i = 0; i < 10; i++) {      const newItem = document.createElement('div');      newItem.className = 'item';      newItem.textContent = `New ${Math.floor(Math.random() * 100)}`;      newItem.style.height = `${Math.floor(Math.random() * 100) + 100}px`;      container.appendChild(newItem);      newItems.push(newItem);    }    layoutItems(newItems);  }  container.style.height = `${Math.max(...columnHeights)}px`;});

其实建树container高度的这句代码应该放到layoutItems函数内部去。

望望后果

图片

转头

好了,原生js手写瀑布流布局到这里就完成了。咱们这里加载更多用的是监听scroll事件,其实还不错荟萃之前的一篇著述,用IntersectionObserver这个api,在页面履行底部放一个loading元素,不雅察这个loading元素与页面的交叉情况。

若是用vue来收场,亦然相通的想路,亦然凭证容器宽度和子元素宽度计较列数,不外不错无谓操作dom了,只用处理数据,把所少见据分红一个二维数组,有几列就有几项,然后处理每一条新数据往哪个子数组里塞。

终末贴一下整理之后的一都js代码就截止了:91porn,com

// script.jswindow.onload = function () {  // 得到父元素dom  const container = document.getElementById('container');  // 列宽  const columnWidth = 200;  // 隔断宽度  const columnGap = 10;  // 父元素自己宽度  const containerWidth = container.offsetWidth;  // 计较最多清楚若干列  const columnCount = Math.floor(containerWidth / (columnWidth + columnGap));  // 开动化每列高度的数组  let columnHeights = Array(columnCount).fill(0);  function layoutItems(items) {    items.forEach(item => {      // 找到高度最矮的那一列的下标      const minColumnIndex = columnHeights.indexOf(Math.min(...columnHeights));      // 现时元素的top值等于最矮的那一列的高度      const top = columnHeights[minColumnIndex];      // 现时元素的left值等于列宽乘以最矮的那一列的下标      const left = minColumnIndex * (columnWidth + columnGap);      item.style.top = `${top}px`;      item.style.left = `${left}px`;      // 及时更新高度(自己高度+隔断)      columnHeights[minColumnIndex] += item.offsetHeight + columnGap;    });    // 这里不给高度的话,会一直触发loadMore    container.style.height = `${Math.max(...columnHeights)}px`;  }  function loadMoreItems() {    const newItems = [];    for (let i = 0; i < 10; i++) {      const newItem = document.createElement('div');      newItem.className = 'item';      newItem.style.height = `${Math.floor(Math.random() * 100) + 100}px`;      container.appendChild(newItem);      newItems.push(newItem);    }    layoutItems(newItems);  }  function handleScroll() {    if (window.innerHeight + window.scrollY >= document.body.offsetHeight) {      loadMoreItems();    }  }  window.addEventListener('scroll', handleScroll);  // 开动布局  const initItems = Array.from(container.getElementsByClassName('item'));  layoutItems(initItems);}; 本站仅提供存储管事,扫数履行均由用户发布,如发现存害或侵权履行,请点击举报。

  
友情链接:

Powered by 自慰女孩 @2013-2022 RSS地图 HTML地图

Copyright Powered by站群系统 © 2013-2024