【鸿蒙实战开发】HarmonyOS瀑布流的实现

33 篇文章 0 订阅
33 篇文章 0 订阅

JS瀑布流的实现

实现思路:

1.首先根据视口的宽度除以元素宽度加间距的宽度,向下取整,获取一行所占列数
2.获取所有子元素数据,循环遍历,当循环索引小于子元素索引,说明处于第一行,否则位于后几行
3.第一行设置绝对定位,设置上、左偏移量,上偏移量0,左偏移量根据索引和元素宽度进行设置,并将元素的offsetHeight存入元素列高度的数组
4.其余行,将后续元素依次放到列高度最小的位置。获取到元素列高度数组中最小的索引,根据列高度和列偏移量分别设置当前元素的上、左偏移量,将当前列高度更新
5.当视口宽度改变时,重新设置列,排列元素位置
6.当容器底部的位置小于或等于视口高度加上滚动距离,说明触底,加载更多的数据

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>瀑布流示例</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      position: relative;
    }

    img {
      display: block;
      width: 250px;
    }

  .item {
      box-shadow: 2px 2px 2px #999;
      position: absolute;
    }
  </style>
  <script>
    window.onload = function () {
      var box = document.getElementById('box');
      var items = box.children;
      var gap = 10;
      var img_width = 250;

      function waterfall() {
        var pageWidth = document.documentElement.clientWidth || document.body.clientWidth;
        // 获取列数
        var column = Math.floor(pageWidth / (img_width + gap));

        var arr = []; 
        for (var i = 0; i < items.length; i++) {
          if (i < column) { 
          // 第一行执行
            items[i].style.top = 0;
            items[i].style.left = (img_width + gap) * i + 'px';
            arr.push(items[i].offsetHeight); 
          } else { 
          // 其余行执行
            var minHeight = arr[0];
            var index = 0;
            for (var j = 0; j < arr.length; j++) {
              if (minHeight > arr[j]) {
                minHeight = arr[j];
                index = j;
              }
            }
            items[i].style.top = arr[index] + gap + 'px'; 
            items[i].style.left = items[index].offsetLeft + 'px'; 
            arr[index] = arr[index] + items[i].offsetHeight + gap; 
          }
        }
        
        var boxBottom = box.getBoundingClientRect().bottom;
        // 触底加载数据
        if (boxBottom <= document.documentElement.clientHeight + window.pageYOffset) {
          for (var k = 0; k < 5; k++) {
            var newItem = document.createElement('div');
            newItem.className = 'item';
            var newImg = document.createElement('img');
            newImg.src = `img/${items.length + k + 1}.jpg`;
            newItem.appendChild(newImg);
            box.appendChild(newItem);
          }
          waterfall();
        }
      }

      waterfall(); 
      // 窗口尺寸变化执行重新绘制
      window.onresize = function () {
        waterfall();
      };

      // 滚动事件监听
      window.addEventListener('scroll', function () {
        waterfall();
      });
    };
  </script>
</head>

<body>
  <div class="container" id="box">
    <div class="item"><img src="img/1.jpg" /></div>
    <div class="item"><img src="img/2.jpg" /></div>
    <div class="item"><img src="img/3.jpg" /></div>
  </div>
</body>

</html>

WaterFllow组件实现

无限滚动

在onReachEnd时给LazyForEach数据源增加新数据,并将footer做成正在加载新数据的样式(使用LoadingProgress组件)。

build() {
  Column({ space: 2 }) {
    WaterFlow({ footer: this.itemFoot.bind(this) }) {
      LazyForEach(this.datasource, (item: number) => {
        FlowItem() {
          Column() {
            Text("N" + item).fontSize(12).height('16')
            Image('res/waterFlowTest (' + item % 5 + ').jpg')
              .objectFit(ImageFit.Fill)
              .width('100%')
              .layoutWeight(1)
          }
        }
        .width('100%')
        .height(this.itemHeightArray[item % 100])
        .backgroundColor(this.colors[item % 5])
      }, (item: string) => item)
    }
    // 触底加载数据  
    .onReachEnd(() => {
      console.info("onReachEnd")
      setTimeout(() => {
        for (let i = 0; i < 100; i++) {
          this.datasource.addLastItem()
        }
      }, 1000)
    })
    .columnsTemplate("1fr 1fr")
    .columnsGap(10)
    .rowsGap(5)
    .backgroundColor(0xFAEEE0)
    .width('100%')
    .height('80%')
  }
}

// 在数据尾部增加一个元素  
public addLastItem(): void {
  this.dataArray.splice(this.dataArray.length, 0, this.dataArray.length)
  this.notifyDataAdd(this.dataArray.length - 1)
}

提前新增数据

想要流畅的进行无限滑动,还需要调整下增加新数据的时机。比如可以在LazyForEach还剩若干个数据就迭代到结束的情况下提前增加一些新数据。

build() {
  Column({ space: 2 }) {
    WaterFlow() {
      LazyForEach(this.datasource, (item: number) => {
        FlowItem() {
          Column() {
            Text("N" + item).fontSize(12).height('16')
            Image('res/waterFlowTest (' + item % 5 + ').jpg')
              .objectFit(ImageFit.Fill)
              .width('100%')
              .layoutWeight(1)
          }
        }
        .onAppear(() => {
          // 即将触底时提前增加数据  
          if (item + 20 == this.datasource.totalCount()) {
            for (let i = 0; i < 100; i++) {
              this.datasource.addLastItem()
            }
          }
        })
        .width('100%')
        .height(this.itemHeightArray[item % 100])
        .backgroundColor(this.colors[item % 5])
      }, (item: string) => item)
    }
    .columnsTemplate("1fr 1fr")
    .columnsGap(10)
    .rowsGap(5)
    .backgroundColor(0xFAEEE0)
    .width('100%')
    .height('80%')
  }
}

固定宽高比

当瀑布流内容较多时,由于避免了组件整体的测算过程,性能会带来一定的提升。

// 计算FlowItem高度
getSize() {
  let ret = Math.floor(Math.random() * this.maxSize)
  return (ret > this.minSize ? ret : this.minSize)
}

// 设置FlowItem的高度数组
setItemSizeArray() {
  for (let i = 0; i < 100; i++) {
    this.itemHeightArray.push(this.getSize())
  }
}

build() {
  Column({ space: 2 }) {
    WaterFlow() {
      LazyForEach(this.datasource, (item: number) => {
        FlowItem() {
          ...
        }
        .width('100%')
        // 从数组中取得数值,设置FlowItem的高度
        .height(this.itemHeightArray[item % 100])
      }, (item: string) => item)
    }
    ...
  }
}

写在最后

●如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我两个小忙:
●点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
●关注小编,同时可以期待后续文章ing ,不定期分享原创知识。

在这里插入图片描述

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值