• 瀑布流布局


    目录

    需求

    思路

    代码实现

    实现效果

    问题和修正

    修正后效果

    总结


    需求

    所谓瀑布流布局,就是含有若干个等宽的列,每一列分别放置图片、视频等,放置的元素都是等宽的,因此可能是不等高的。新的元素到来时,会插入高度较低的那一列,这样形成参差的、视觉上像瀑布一样的布局。

    这里简化一下,只要两列等宽布局展示图片即可。

    思路

    两列布局,直接使用flex布局实现即可。不过,这里不能设置align-items为center,如果设置了将会使图片列居中显示,不符合瀑布流的视觉效果。我设置left和right两列,两列的宽度相等,结构和样式基本就写完了。

    然后写JavaScript。其逻辑是,判断当前left和right的高度(我使用clientHeight),如果左边<=右边,则放入左边,否则放入右边。遍历所有的图片,按照这个逻辑放入即可。

    代码实现

    html部分

    1. <div class="container">
    2. <div class="col left">div>
    3. <div class="col right">div>
    4. div>

    css部分

    1. .container {
    2. width: 700px;
    3. background-color: aliceblue;
    4. margin: auto;
    5. /* flex布局 */
    6. display: flex;
    7. align-items: flex-start;
    8. }
    9. .col {
    10. flex-basis: 350px;
    11. }
    12. .col img {
    13. /* 固定图片的宽度 */
    14. width: 100%;
    15. }

    JavaScript部分

    1. // 获取三个元素
    2. let container = document.getElementsByClassName('container')[0]
    3. let left = document.getElementsByClassName('col')[0]
    4. let right = document.getElementsByClassName('col')[1]
    5. // 插入图片
    6. function initImg() {
    7. for (let i = 1; i < 27; i++) {
    8. let img = new Image();
    9. img.src = "./pictures/" + i + ".jpg"
    10. if (left.clientHeight <= right.clientHeight) {
    11. left.appendChild(img)
    12. } else {
    13. right.appendChild(img)
    14. }
    15. }
    16. }
    17. initImg()

    如代码所示,获取了父元素和左右两列,然后遍历每一张图片,依次判断插入即可。看上去很完美,但是真的如此吗?

    实现效果

     看上去貌似很完美,也有瀑布的样子。但当拉到页面底部发现:

    左边的一大块都是空的,全部放在了右边。这显然不对,因为按照逻辑,左边更短,应该加在左边才对。

    问题和修正

    问题就在于,img的加载是个异步的过程。再看刚才的for循环:

    1. // 插入图片
    2. function initImg() {
    3. for (let i = 1; i < 27; i++) {
    4. let img = new Image();
    5. img.src = "./pictures/" + i + ".jpg"
    6. if (left.clientHeight <= right.clientHeight) {
    7. left.appendChild(img)
    8. } else {
    9. right.appendChild(img)
    10. }
    11. }
    12. }

    new了Image对象后,指定了其src,然后立刻判断左右两边的高度。这时,img还没有加载完毕。然而,for循环不会等待它加载完毕。下一张图片产生后,立刻也会判断,但此时上一张图片还没有放到页面上,所以左右的高度很可能就是错误的,从而产生了错误的判断。这就出现了上图中,右列出现了很多很多图片,而左列空白的样子。

    解决办法就是,把这个循环写成异步的,只有上一张图片加载完毕后,才去判断下一张图片。

    很容易想到,使用Promise完成异步判断。但是对于循环中的Promise,很难清楚地通过then的变化来推进。于是,我决定采用async和await。

    这就需要再封装一个方法,这个方法返回Promise,在Promise中加载某一张图片。然后再遍历所有图片,使用async/await,依次调用这个方法,就能得到结果了。

    而在Promise中,我们在什么时候调用resolve呢?这就需要监听img的onload事件,设置onload事件的回调函数,在回调函数中调用resolve即可。

    经过分析,再次完善代码:

    1. // 加载第index张图片
    2. function loadIndexImg(index) {
    3. return new Promise((resolve, reject) => {
    4. // 当前加载的图片
    5. let img = new Image();
    6. img.src = './pictures/' + index + '.jpg'
    7. img.onload = () => {
    8. if (left.clientHeight <= right.clientHeight) {
    9. left.appendChild(img)
    10. } else {
    11. right.appendChild(img)
    12. }
    13. resolve();
    14. }
    15. })
    16. }
    17. // 插入图片
    18. async function initImg() {
    19. // 要在加载完并插入图片后才去判断下一张,因此采用async/await
    20. for (let i = 1; i <= 26; i++) {
    21. await loadIndexImg(i)
    22. }
    23. }
    24. initImg()

    可以看到,在initImg中,依次调用loadIndexImg,并且是异步调用。在图片加载完成后再去加载下一张图片,效果应该就可以了。

    修正后效果

    效果很好!!!

    总结

    本文实现了简单的两列瀑布流布局,其中需要用到Promise等异步操作。

  • 相关阅读:
    【Linux 操作系统】I /O输入与输出
    海泰方圆成功举办“引领数据安全创新,加速数字经济发展”技术研讨会
    【C++ 拷贝构造函数详解】
    DC电源模块具有不同的安装方式和安全规范
    grpc-java源码环境编译
    arm函数栈帧(stackframe)结构和传参规则
    Vue Express结合MySQL项目
    UE4中抛体物理模拟UProjectileMovementComponent
    Himall商城文件帮助类IOHelper(2)
    MyBatis实现延时加载的方式
  • 原文地址:https://blog.csdn.net/weixin_45792464/article/details/125872542