手把手教你用原生JS实现瀑布流

一、什么是瀑布流布局

当我们浏览一些图片网站时会发现一些图片宽度相同、高度不同,确能够自动地适应,达到一行一行展示的效果。并且当浏览器滑到底部时,新加载的图片也会自动适应。这一效果就是瀑布流布局。瀑布流布局的特点是图片等宽不等高

二、瀑布流布局效果展示

  • 三列
    在这里插入图片描述
  • 四列在这里插入图片描述
  • 五列
    在这里插入图片描述

三、瀑布流布局原理

实现瀑布流布局最重要的就是怎么用JS使子盒子定位到合适的位置
接下来我用下面这个示意图向大噶解释瀑布流的基本原理(图丑别介意哈哈)

注意
(1)红色盒子是按顺序排在第一行的子盒子
(2)从第二行开始,将第二行的第一个子盒子追加在第一行最矮盒子的后面;将第二行的第二个子盒子追加在第一行第二矮矮盒子的后面…依次类推。 (下面第一张图)
(3)新追加子盒子的位置要相对父盒子进行定位。其 左距离下标值 * 子盒子宽度上距离 为其所跟子盒子的高度 (下面第二张图)
(4)排列好第二行的子盒子后将前两行同列子盒子的高度相加看作新的一行子盒子的高度,再重复上述步骤。 (下面第三张图)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

四、HTML页面布局

很简单的一个页面布局,大噶都能看的懂哈(我这里使用了十六张图片),就不啰(tou)嗦(lan)啦!

<div id="main">
  <div class="box">
    <div class="pic">
      <img src="../program1/images/1.jpg" alt="">
    </div>
  </div>
  <div class="box"><div class="pic"><img src="../program1/images/2.jpg" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/3.png" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/4.png" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/1.jpg" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/2.jpg" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/3.png" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/4.png" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/1.jpg" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/2.jpg" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/3.png" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/4.png" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/1.jpg" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/2.jpg" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/3.png" alt=""></div></div>
  <div class="box"><div class="pic"><img src="../program1/images/4.png" alt=""></div></div>
</div>

五、CSS样式布局

这里提一些大家需要注意的地方
(1)选择器main 要设置 position: relative; 是为了后续一部分子盒子要进行定位操作(子绝父相)
(2)选择器box 要设置 float: left; 是因为 div 标签是块状元素,使用 float: left; 使其左浮

<style>
  * {
    padding: 0;
    margin: 0;
    border: none;
  }
  img {
    width: 200px;
    vertical-align: top;
  }
  #main {
    position: relative;
  }
  .box {
    float: left;
    padding: 15px 0 0 15px;
  }
  .pic {
    padding: 10px;
    border: 1px solid #ccc;
  }
</style>

六、JS核心代码

1、全部JS代码展示

window.addEventListener("load",function() {
  //1、实现瀑布流布局
  waterFall('main','box');
  })
})

/*
  实现瀑布流布局,传递参数为string类型
*/
function waterFall(parent,child) {
  //1、根据图片的列数来确定父盒子的宽度,父盒子居中
  //1.1 获取标签,父盒子和所有子盒子
  var father= document.getElementById(parent);
  var allBox = document.getElementsByClassName(child); 
  //1.2 获取其中一个的宽度
  var boxWidth = allBox[0].offsetWidth;
  //1.3 获取文档的宽度(兼容)
  var screen = document.documentElement.clientWidth || document.body.clientWidth;
  //1.4 求出当前图片的列数,是变化的
  var cols = parseInt(screen / boxWidth);
  //1.5 父盒子居中,给父盒子设置宽度
  father.style.width = cols * boxWidth + 'px';
  father.style.margin = '0 auto';

  //2、子盒子定位(从第二行开始)
  //2.1 定义变量
   var heightArr = [], boxHeight = 0, minBoxHeight = 0, minIndex = 0;
  //2.2 遍历所有的盒子
  for(let i = 0;i < allBox.length; i++) {
    boxHeight = allBox[i].offsetHeight;
    //2.3 判断是否是第一行
    if(i < cols) {
      heightArr.push(boxHeight)
    }else { //剩余行做定位
      //2.4 取出数组中最矮盒子的高度
      minBoxHeight = heightArr[minBox(heightArr)];
      //2.5 取出最矮盒子再数组中的索引
      minIndex = minBox(heightArr);
      //2.6 剩余子盒子的定位
      allBox[i].style.position = 'absolute';
      allBox[i].style.left = minIndex * boxWidth + 'px';
      allBox[i].style.top = minBoxHeight + 'px';
      //2.7 更新高度
      heightArr[minIndex] += boxHeight;
    }
  }
  
}

function minBox(box) {
  var j = 0;
  for(i in box) {
    if(box[j] > box[i])
      j = i
  }
  return j;
} 

2、JS代码详解----入口函数

在入口函数中调用函数 waterFall() 实现瀑布流布局。传递两个参数,都为 String 类型。第一个参数是父盒子的选择器名,第二个参数是子盒子的选择器名。

window.addEventListener("load",function() {
  //1、实现瀑布流布局
  waterFall('main','box');
  })
})

3、JS代码详解----父盒子居中

在css代码中我们并没有让父盒子居中,是因为父盒子宽度是随着浏览器宽度的改变而改变的。因此在 waterFall() 实现瀑布流函数中我们首先要做的就是使父盒子居中。

这里提一些大家需要注意的地方
(1)获取文档的宽度时推荐写兼容写法(大噶可以参考:Scroll家族(写法类似)
(2)boxWidth 是每一个子盒子的宽度,因为瀑布流布局的特点是图片的宽度相同的,高度不同,所以每一个子盒子的宽度都是相同的。
(3)设置父盒子的宽度,其宽度即子盒子所占列数 * 子盒子的宽度
(4)设置父盒子 margin = '0 auto’ 达到父盒子居中的效果

注释部分需要大噶特别注意

//1、根据图片的列数来确定父盒子的宽度,父盒子居中
  //1.1 获取标签,父盒子和所有子盒子
  var father= document.getElementById(parent);
  var allBox = document.getElementsByClassName(child); 
  //1.2 获取其中一个的宽度
  var boxWidth = allBox[0].offsetWidth;
  //1.3 获取文档的宽度(兼容)
  var screen = document.documentElement.clientWidth || document.body.clientWidth;
  //1.4 求出当前图片的列数,是变化的
  var cols = parseInt(screen / boxWidth);
  //1.5 父盒子居中,给父盒子设置宽度
  father.style.width = cols * boxWidth + 'px';
  father.style.margin = '0 auto';

4、JS代码详解----子盒子定位

这里提一些大家需要注意的地方
(1)根据瀑布流布局的原理,我们需要把每一列子盒子的高度存放在一个数组中,即 heightArr,将新的子盒子每次追加在最小高度的子盒子的后面。这一步骤使用 for循环* 和 if判断 语句就可以实现
(2)函数 minBox(box) 可以传递一个数组类型的参数,作用是找到数组中的最小值,并且特别注意返回的是最小值的下标
(3)设置新的子盒子的定位,首先要设置 position = ‘absolute’; (子绝父相)。其 left 即为 下标值 * 子盒子宽度top 值为其所跟子盒子的高度。
(4)更新高度作为该一列的高度。

注释部分需要大噶特别注意

  //2、子盒子定位(从第二行开始)
  //2.1 定义变量
   var heightArr = [], boxHeight = 0, minBoxHeight = 0, minIndex = 0;
  //2.2 遍历所有的盒子
  for(let i = 0;i < allBox.length; i++) {
    boxHeight = allBox[i].offsetHeight;
    //2.3 判断是否是第一行
    if(i < cols) {
      heightArr.push(boxHeight)
    }else { //剩余行做定位
      //2.4 取出数组中最矮盒子的高度
      minBoxHeight = heightArr[minBox(heightArr)];
      //2.5 取出最矮盒子再数组中的索引
      minIndex = minBox(heightArr);
      //2.6 剩余子盒子的定位
      allBox[i].style.position = 'absolute';
      allBox[i].style.left = minIndex * boxWidth + 'px';
      allBox[i].style.top = minBoxHeight + 'px';
      //2.7 更新高度
      heightArr[minIndex] += boxHeight;
    }
  }
function minBox(box) {
  var j = 0;
  for(i in box) {
    if(box[j] > box[i])
      j = i
  }
  return j;
} 

七、升级版

上面已经实现了瀑布流布局,但发现浏览器中16张图片展示完后不会再有图片展示。在这里将在以上代码的基础上用Dock数据加载解决这个问题(实际中用ajax)

1、入口函数更改

在入口函数中 增加浏览器滚动的监听器 ,其内完成加载新的数据的功能
在实现瀑布流函数后增加自定义追加 check()函数scroll(0兼容性函数

这里提一些大家需要注意的地方
(1)在浏览器滚动的监听器首先要使用 check()函数 检查是否需要增加 新数据
(2)在这里使用的是一个数组(实质是假数据)实现新数据加载的功能
(3)遍历数组,创造新的节点追加到浏览器中
(4)追加新数据后重新进行瀑布流布局

window.addEventListener("load",function() {
  //1、实现瀑布流布局
  waterFall('main','box');

  //2、加载数据(追加)
  window.addEventListener('scroll',function() {
    if(check()) {
      //2.1 假数据
      var dataArr = [
        {"src":"../program1/images/1.jpg"},
        {"src":"../program1/images/2.jpg"},
        {"src":"../program1/images/3.png"},
        {"src":"../program1/images/4.png"},
      ];
      //2.2 遍历数据
      for(var i = 0;i < dataArr.length; i++) {
        var newBox = document.createElement('div');
        newBox.className = 'box';
        document.getElementById('main').appendChild(newBox);

        var newPic = document.createElement('div');
        newPic.className = 'pic';
        newBox.appendChild(newPic);

        var newImg = document.createElement('img');
        newImg.src = dataArr[i].src;
        newPic.appendChild(newImg);
      }
      
      //重新进行瀑布流布局
      waterFall('main','box');
    }
  });
});

2、追加检查函数和scroll兼容性写法

(1)check()函数true 的条件是 数组最后一个元素的高度 + 盒子高度的一半 <= 页面高度 + 页面滚出浏览器的高度(偏移高度)
(2)scroll()函数 的作用是得到页面滚出浏览器的高度的兼容性写法推荐参考:Scroll家族

//追加在最矮盒子的后面
function check() {
  //1、获取最后的盒子
  var allBox = document.getElementsByClassName('box');
  var lastBox = allBox[allBox.length - 1];
  //2、 最后盒子高度的一半
  var lastBoxDis = lastBox.offsetHeight*0.5 + lastBox.offsetTop;

  //3、页面高度
  var screenH = document.documentElement.clientHeight || document.body.clientHeight;

  //4、求出页面滚出浏览器的高度(偏移高度)
  var scrollTop = scroll().top;

  //5、返回结构
  return lastBoxDis <= screenH + scrollTop;
  
}

//兼容性
function scroll() {
  if(window.pageYOffset != null){
      //返回一个字面量对象(JSON对象,JSON中的键必须有双引号)
      return {
          "top":window.pageYOffset,
          "left":window.pageXOffset
      }
  }else if (document.compatMode === 'CSS1Compat'){     //W3C
      return {
          "top":document.documentElement.scrollTop,
          "left":document.documentElement.scrollLeft
      }
  }else {
      return {
          "top":document.body.scrollTop,
          "left":document.body.scrollLeft
      }
  }
}

大噶若觉得这篇blog有帮助到你,走过路过点个👍呗
若有啥地方需要改进,也请大佬多多提点

  • 23
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
要用Pytorch手写一个Transformer模型,可以按照以下步骤进行: 1. 导入所需的库和模块,包括torch、torch.nn、torch.nn.functional以及Transformer模型所需的子模块如EncoderLayer和DecoderLayer。 2. 定义Transformer模型的编码器部分。编码器由多个EncoderLayer组成,每个EncoderLayer包含自注意力机制(Self-Attention)、前馈神经网络和残差连接。 3. 定义Transformer模型的解码器部分。解码器也由多个DecoderLayer组成,每个DecoderLayer包含自注意力机制、编码器-解码器注意力机制和前馈神经网络。 4. 定义Transformer模型本身。它包含编码器和解码器,以及最后的线性层用于生成输出。 5. 实现模型的前向传播函数。在前向传播函数中,输入数据将分别经过编码器和解码器,并返回最后的输出。 6. 初始化模型并定义损失函数和优化器。 7. 定义训练循环。在每个训练迭代中,将输入数据传递给模型进行前向传播,计算损失值,并进行反向传播和参数更新。 8. 进行模型训练。根据实际情况,可以调整超参数、训练数据和训练次数等。 请注意,以上步骤是一个大致的框架,具体的实现细节可能会有所不同。可以参考引用中提到的huggingface提供的transformer模型代码,以及Transformer模型的论文《Attention is All You Need》来进行更详细的实现。 huggingface官方文档: [link] Transformer模型图: [link]<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [手把手教你用Pytorch代码实现Transformer模型(超详细的代码解读)](https://blog.csdn.net/qq_43827595/article/details/120394042)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值