资讯专栏INFORMATION COLUMN

小程序之图片瀑布流(最全实现方式,额外加送懒加载)

rubyshen / 2077人阅读

摘要:完整代码请戳我们回到小程序,此时接口返回的数据如下可以看到每个图片都有高度了,接下来我们实现瀑布流布局,等下,我们搞下瀑布流布局的懒加载,关于小程序的懒加载,猛戳了解更多。

效果图

来来来,看啊看,外面的世界多好看,

效果图展示的是瀑布流布局 && 懒加载的效果

数据

图片数据来源张鑫旭的网络日志

先说下我们的图片链接格式

所有的链接都是http://cued.xunlei.com/demos/publ/img/P_${name}.jpg这样的格式,我们需要改变name的值就行了,当name值小于10的时候,格式是00x,如002003,大于10的时候就是023这种。

定义

瀑布流布局是一种比较流行的页面布局方式, 最早采用此布局的网站是Pinterest, 图片宽度是固定的,高度自动,产生一种参差不齐的美感。

原理

原理很简单,主要分为以下几步

1、定义高度数组和列数

2、遍历元素,个数小于列数的直接push到数组中

3、大于列数的,获取高度数组中最小的值,定义元素的top和left值

4、重要一点 更新高度数组,将最小高度加上当前元素的高度

知道原理了,代码应该怎么写呢?这里用web端来示例,大概如下

</>复制代码

  1. let heightArr = []
  2. let col = 2
  3. let allBox = document.querySelectorAll(".box") // 获取所有盒子
  4. for(let i in allBox){
  5. let boxWidth = allBox[0].offsetWidth // 获取盒子宽度 都一样直接取第一个
  6. let boxHeight = allBox[i].offsetHeight
  7. if(i < col){
  8. heightArr.push(boxHeight) // 把第一行高度都添加进去
  9. } else { // 进行布局操作
  10. let minHeight = Mac.min.apply(null, heightArr) // 获取最小高度
  11. let minIndex = getIndex(heightArr, minHeight) // 获取最小高度的下标 要不就是0 要不就是1
  12. allBox[i].style.position = "absolute"
  13. allBox[i].style.top = minHeight + "px"
  14. allBox[i].style.width = minIndex * boxWidth + "px"
  15. heightArr[minIndex] += boxHeight // 更新最新高度
  16. }
  17. }
  18. // 获取下标
  19. getIndex(arr, val){
  20. for(i in arr){
  21. if(arr[i] == val) {
  22. return i
  23. }
  24. }
  25. }

上面就是实现瀑布流的主要逻辑,这里大概写了下,接下来我们看看小程序怎么实现。

实现

在web页面里面我们可以直接获取、操作DOM,实现起来很方便,何况还有很多的jquery插件可以使用。我们知道小程序里面是没有DOM的,那应该怎么实现呢?我们把思路转换下就行了。

这里我们用三种方式来实现瀑布流布局。

CSS

使用css3来实现是最简单的,我们先捡简单的来说,

使用column-count属性设置列数

使用wx-if进行判断将图片渲染到左侧还是右侧

wxml

</>复制代码

wxss

</>复制代码

  1. .container{
  2. column-count: 2; /*设置列数*/
  3. column-gap:2rpx;
  4. padding-left: 8rpx;
  5. }
  6. image{
  7. width: 182px;
  8. box-shadow: 2px 2px 4px rgba(0,0,0,.4);
  9. }

js获取下数据即可,这里就不赘述了。

节点信息

小程序可以通过WXML节点信息API来获取元素的信息,接下来我们来撸码。

wxml

</>复制代码

wxss

</>复制代码

  1. .container{
  2. position: relative;
  3. display: flow-root;
  4. }
  5. .box{
  6. float: left;
  7. display: flex;
  8. margin-left:5rpx;
  9. box-shadow: 2rpx 2rpx 5rpx rgba(0,0,0,.3);
  10. border: 1rpx solid #ccc;
  11. box-sizing: border-box;
  12. padding: 10px;
  13. }
  14. .box:nth-child(2){
  15. margin-left: 12rpx;
  16. }
  17. image{
  18. width: 100%;
  19. }
js

图片链接为http://cued.xunlei.com/demos/publ/img/P_${name}.jpg, 只需要更改name就行了

首先处理我们的数据

</>复制代码

  1. // 创建长度为30的数组
  2. const mockData = () => {
  3. return Array.from(Array(30).keys()).map(item => {
  4. if (item < 10) {
  5. return "00" + item
  6. } else {
  7. return "0" + item
  8. }
  9. })
  10. }
  11. // 扩展成我们需要的数据
  12. const createGroup = () => {
  13. let group = []
  14. let list = mockData()
  15. list.forEach(item => {
  16. group.push({ name: item, position: "static", top: "", left: "" })
  17. })
  18. return group
  19. }

然后进行瀑布流布局,主要代码如下

</>复制代码

  1. load(e){ // 监听图片加载完 获取图片的高度
  2. this.setData({
  3. height: [...this.data.height, e.detail.height]
  4. })
  5. this.showImg() // 调用渲染函数
  6. },
  7. showImg(){
  8. let height = this.data.height
  9. if (height.lenth != this.data.group .legth){ // 保证所有图片加载完
  10. return
  11. }
  12. setTimeout(()=>{ // 异步执行
  13. wx.createSelectorQuery().selectAll(".box").boundingClientRect((ret) => {
  14. let cols = 2
  15. var group = this.data.group
  16. var heightArr = [];
  17. for (var i = 0; i < ret.length; i++) {
  18. var boxHeight = height[i]
  19. if (i < cols) {
  20. heightArr.push(boxHeight + 25)
  21. } else {
  22. var minBoxHeight = Math.min.apply(null, heightArr);
  23. var minBoxIndex = getMinBoxIndex(minBoxHeight, heightArr);
  24. group[i].position = "absolute"
  25. group[i].top = `${minBoxHeight}px`
  26. group[i].left = minBoxIndex * this.data.width / 2 + "px"
  27. group[i].left = minBoxIndex == 0 ? minBoxIndex * this.data.width / 2 + "px" : minBoxIndex * this.data.width / 2 + 5 + "px"
  28. heightArr[minBoxIndex] += (boxHeight + 25)
  29. }
  30. }
  31. this.setData({
  32. group
  33. })
  34. wx.hideLoading()
  35. }).exec()
  36. }, 200)
  37. }

可以看到实现的逻辑和上面的大概类似,只不过这里我们修改的是数据,毕竟小程序是数据驱动的嘛。

这里主要我们监听image组件的bindload事件来获取每张图片的高度,获取了高度才能进行布局,大部分的时间也都用来加载图片了,能不能优化呢?当然可以了,我们使用node把数据包装下。

后端处理数据

上面我们说到在小程序内部获取图片的高度是个费力不讨好的事,我们使用node来获取图片高度,然后包装下再给小程序使用。

使用request进行请求

使用image-size获取图片的高度

最后将获取后将数据写入文件,启动一个服务提供接口

这里主要说下碰到的问题

1、request模块的请求默认返回来的是个String类型的字符串,使用image-size模块传入的必须是Buffer,怎么破呢?在request请求中设置encodingnull即可

2、我们这里爬取了100张图片,怎么保证都已经爬取完了呢?可以这样写

</>复制代码

  1. Promise.all(List.map(item => getImgData(item))) // getImgData函数是获取图片的函数 会返回个promise

3、如果请求了几次,发现有的图片获取不到了,报错了,怎么回事呢,人家毕竟做了防爬的,恭喜你中奖了,换个ip再试吧(可以把代码放在服务器上面,或者换个Wi-Fi),其实我们只需要爬一次就行,生成完文件还爬干嘛啊。

完整代码请戳github

我们回到小程序,此时接口返回的数据如下

可以看到每个图片都有高度了,接下来我们实现瀑布流布局,等下,我们搞下瀑布流布局的懒加载,关于小程序的懒加载,猛戳了解更多。

怎么实现呢?主要分为两步

1、将元素瀑布流布局

2、创建IntersectionObserver,进行懒加载

先开始我们的布局吧

wxml

</>复制代码

上面我们使用wx-if通过show这个字段来进行判断了图片是否加载,

使用一个view组件用来占位,然后更改show字段就可以显示图片了

js

我们使用两个for循环,先来进行布局

</>复制代码

  1. let cols = 2
  2. let list = this.data.list
  3. let heightArr = [];
  4. for(let i in list){
  5. var boxHeight = list[i].height
  6. if (i < cols) {
  7. heightArr.push(boxHeight + 5)
  8. } else {
  9. var minBoxHeight = Math.min.apply(null, heightArr);
  10. var minBoxIndex = getMinBoxIndex(minBoxHeight, heightArr);
  11. list[i].position = "absolute"
  12. list[i].top = `${minBoxHeight}px`
  13. list[i].left = minBoxIndex * 182 + "px"
  14. list[i].left = minBoxIndex == 0 ? minBoxIndex * 182 + "px" : minBoxIndex * 182 + 4 + "px"
  15. heightArr[minBoxIndex] += (boxHeight + 5)
  16. }
  17. }
  18. this.setData({
  19. list
  20. })

布局完后,创建IntersectionObserver,动态判断image节点的显示

</>复制代码

  1. for (let i in list) {
  2. wx.createIntersectionObserver().relativeToViewport({ bottom: 20 }).observe(".pic-" + i, (ret) => {
  3. if (ret.intersectionRatio > 0) {
  4. list[i].show = true
  5. }
  6. this.setData({
  7. list
  8. })
  9. })
  10. }
最后

我们使用三种方式完成了小程序的瀑布流布局,还额外完成了基于瀑布流的懒加载。可以发现使用css最简便,虽然小程序不能操作DOM,但是我们改完数据其实和改变DOM一样,将观念转变过来,小程序的开发还是很爽的。

最后的最后,各位,周末快乐。

github

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95026.html

相关文章

  • 原生 JS 实现一个瀑布插件

    摘要:瀑布流布局中的图片有一个核心特点等宽不定等高,瀑布流布局在国内网网站都有一定规模的使用,比如花瓣网等等。那么接下来就基于这个特点开始瀑布流探索之旅。 showImg(https://segmentfault.com/img/remote/1460000013059759?w=640&h=280); 瀑布流布局中的图片有一个核心特点 —— 等宽不定等高,瀑布流布局在国内网网站都有一定规模...

    Alfred 评论0 收藏0
  • 原生 JS 实现一个瀑布插件

    摘要:瀑布流布局中的图片有一个核心特点等宽不定等高,瀑布流布局在国内网网站都有一定规模的使用,比如花瓣网等等。那么接下来就基于这个特点开始瀑布流探索之旅。 showImg(https://segmentfault.com/img/remote/1460000013059759?w=640&h=280); 瀑布流布局中的图片有一个核心特点 —— 等宽不定等高,瀑布流布局在国内网网站都有一定规模...

    lavnFan 评论0 收藏0
  • 原生 JS 实现一个瀑布插件

    摘要:瀑布流布局中的图片有一个核心特点等宽不定等高,瀑布流布局在国内网网站都有一定规模的使用,比如花瓣网等等。那么接下来就基于这个特点开始瀑布流探索之旅。 showImg(https://segmentfault.com/img/remote/1460000013059759?w=640&h=280); 瀑布流布局中的图片有一个核心特点 —— 等宽不定等高,瀑布流布局在国内网网站都有一定规模...

    wenyiweb 评论0 收藏0
  • 程序瀑布效果,解决左右两边高度差距过大的问题

    摘要:参考小红书的瀑布流效果,小红书是分左右两栏的,按照奇数偶数来显示就可以。但是问题来了,随着每个元素高度的不确定性,很大几率会出现左右两栏高度相差大的问题。 想要实现瀑布流的布局效果,并且是按照从左到右顺序显示的话,css布局方式暂时还不能满足我们的需求。参考小红书的瀑布流效果,小红书是分左右两栏的,按照奇数偶数来显示就可以。 ...

    CoffeX 评论0 收藏0

发表评论

0条评论

rubyshen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<