小程序之圖片瀑布流(最全實(shí)現(xiàn)方式蒋畜,額外加送懶加載)

效果圖

來(lái)來(lái)來(lái),看啊看啄栓,外面的世界多好看娄帖,

image

效果圖展示的是瀑布流布局 && 懶加載的效果

數(shù)據(jù)

圖片數(shù)據(jù)來(lái)源張?chǎng)涡竦木W(wǎng)絡(luò)日志

先說(shuō)下我們的圖片鏈接格式

所有的鏈接都是http://cued.xunlei.com/demos/publ/img/P_${name}.jpg這樣的格式,我們需要改變name的值就行了昙楚,當(dāng)name值小于10的時(shí)候近速,格式是00x,如002003堪旧,大于10的時(shí)候就是023這種数焊。

定義

瀑布流布局是一種比較流行的頁(yè)面布局方式, 最早采用此布局的網(wǎng)站是Pinterest, 圖片寬度是固定的,高度自動(dòng)崎场,產(chǎn)生一種參差不齊的美感佩耳。

原理

原理很簡(jiǎn)單,主要分為以下幾步

1谭跨、定義高度數(shù)組和列數(shù)

2干厚、遍歷元素,個(gè)數(shù)小于列數(shù)的直接push到數(shù)組中

3螃宙、大于列數(shù)的蛮瞄,獲取高度數(shù)組中最小的值,定義元素的top和left值

4谆扎、重要一點(diǎn) 更新高度數(shù)組挂捅,將最小高度加上當(dāng)前元素的高度

知道原理了,代碼應(yīng)該怎么寫(xiě)呢堂湖?這里用web端來(lái)示例闲先,大概如下

let heightArr = []
let col = 2
let allBox = document.querySelectorAll('.box') // 獲取所有盒子

for(let i in allBox){
    
    let boxWidth = allBox[0].offsetWidth  // 獲取盒子寬度 都一樣直接取第一個(gè)
  let boxHeight = allBox[i].offsetHeight
    if(i < col){    
        heightArr.push(boxHeight)  // 把第一行高度都添加進(jìn)去
    } else {  // 進(jìn)行布局操作
        
        let minHeight = Mac.min.apply(null, heightArr)  // 獲取最小高度
        let minIndex = getIndex(heightArr, minHeight)  // 獲取最小高度的下標(biāo) 要不就是0 要不就是1
        allBox[i].style.position = 'absolute'
        allBox[i].style.top = minHeight + 'px'
        allBox[i].style.width = minIndex * boxWidth + 'px'
        
        heightArr[minIndex] += boxHeight // 更新最新高度 
    }
}

// 獲取下標(biāo)
getIndex(arr, val){
    for(i in arr){
        if(arr[i] == val) {
            return i
        }
    }
}

上面就是實(shí)現(xiàn)瀑布流的主要邏輯,這里大概寫(xiě)了下无蜂,接下來(lái)我們看看小程序怎么實(shí)現(xiàn)伺糠。

實(shí)現(xiàn)

在web頁(yè)面里面我們可以直接獲取、操作DOM斥季,實(shí)現(xiàn)起來(lái)很方便训桶,何況還有很多的jquery插件可以使用。我們知道小程序里面是沒(méi)有DOM的酣倾,那應(yīng)該怎么實(shí)現(xiàn)呢舵揭?我們把思路轉(zhuǎn)換下就行了。

這里我們用三種方式來(lái)實(shí)現(xiàn)瀑布流布局躁锡。

CSS

使用css3來(lái)實(shí)現(xiàn)是最簡(jiǎn)單的午绳,我們先撿簡(jiǎn)單的來(lái)說(shuō),

使用column-count屬性設(shè)置列數(shù)

使用wx-if進(jìn)行判斷將圖片渲染到左側(cè)還是右側(cè)

wxml

<view class='container'>
    <image src='{{item.url}}' wx:if="{{index % 2 != 0 }}" wx:for="{{list}}" mode='widthFix' wx:key="{{index}}"></image>
     <image src='{{item.url}}' wx:if="{{index % 2 == 0 }}" wx:for="{{list}}" mode='widthFix' wx:key="{{index}}"></image> 
</view> 

wxss

.container{
  column-count: 2;  /*設(shè)置列數(shù)*/ 
  column-gap:2rpx;
  padding-left: 8rpx;
}
image{
  width: 182px;
  box-shadow: 2px 2px 4px rgba(0,0,0,.4);
}

js獲取下數(shù)據(jù)即可稚铣,這里就不贅述了箱叁。

節(jié)點(diǎn)信息

小程序可以通過(guò)WXML節(jié)點(diǎn)信息API來(lái)獲取元素的信息墅垮,接下來(lái)我們來(lái)擼碼。

wxml

<view class="container">
      <view wx:for="{{group}}" style='position:{{item.position}}; top: {{item.top}}; left:{{item.left}}; width:{{width}}rpx;' class='box box-{{index}}' wx:key="{{index}}">
            <image  src='http://cued.xunlei.com/demos/publ/img/P_{{item.name}}.jpg' style=' height:{{height[index]}}px' bindload='load' data-index='{{index}}' class='image'></image>
      </view>
</view> 

wxss

.container{
  position: relative;
  display: flow-root;
}
.box{
  float: left;
  display: flex;
  margin-left:5rpx;
  box-shadow: 2rpx 2rpx 5rpx rgba(0,0,0,.3);
  border: 1rpx solid #ccc;
  box-sizing: border-box;
  padding: 10px;
}
.box:nth-child(2){
  margin-left: 12rpx;
}
image{
  width: 100%;
}

js

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

首先處理我們的數(shù)據(jù)

// 創(chuàng)建長(zhǎng)度為30的數(shù)組
const mockData = () => {
  return Array.from(Array(30).keys()).map(item => {
    if (item < 10) {
      return '00' + item
    } else {
      return '0' + item
    }
  })

}
// 擴(kuò)展成我們需要的數(shù)據(jù)
const createGroup = () => {
  let group = []
  let list = mockData()
  list.forEach(item => {
    group.push({ name: item, position: 'static', top: '', left: '' })
  })
  return group
}

然后進(jìn)行瀑布流布局耕漱,主要代碼如下

load(e){  // 監(jiān)聽(tīng)圖片加載完 獲取圖片的高度
    this.setData({
      height: [...this.data.height, e.detail.height]
    })
    this.showImg()  // 調(diào)用渲染函數(shù)
},

showImg(){
    let height = this.data.height
    if (height.lenth != this.data.group .legth){  // 保證所有圖片加載完
      return
    }
    setTimeout(()=>{ // 異步執(zhí)行
      wx.createSelectorQuery().selectAll('.box').boundingClientRect((ret) => {
        let cols = 2
        var group = this.data.group
        var heightArr = [];
        for (var i = 0; i < ret.length; i++) {
          var boxHeight = height[i]
          if (i < cols) {
            heightArr.push(boxHeight + 25)
          } else {
            var minBoxHeight = Math.min.apply(null, heightArr);
            var minBoxIndex = getMinBoxIndex(minBoxHeight, heightArr);
            group[i].position = 'absolute'
            group[i].top = `${minBoxHeight}px`
            group[i].left = minBoxIndex * this.data.width / 2 + 'px'
            group[i].left = minBoxIndex == 0 ?  minBoxIndex * this.data.width / 2 + 'px' : minBoxIndex * this.data.width / 2 + 5 + 'px'
            heightArr[minBoxIndex] += (boxHeight + 25)
          }
        }

        this.setData({
          group
        })
        wx.hideLoading()

      }).exec()
    }, 200)
    
  }

可以看到實(shí)現(xiàn)的邏輯和上面的大概類(lèi)似算色,只不過(guò)這里我們修改的是數(shù)據(jù),畢竟小程序是數(shù)據(jù)驅(qū)動(dòng)的嘛螟够。

這里主要我們監(jiān)聽(tīng)image組件的bindload事件來(lái)獲取每張圖片的高度灾梦,獲取了高度才能進(jìn)行布局,大部分的時(shí)間也都用來(lái)加載圖片了妓笙,能不能優(yōu)化呢若河?當(dāng)然可以了,我們使用node把數(shù)據(jù)包裝下寞宫。

后端處理數(shù)據(jù)

上面我們說(shuō)到在小程序內(nèi)部獲取圖片的高度是個(gè)費(fèi)力不討好的事萧福,我們使用node來(lái)獲取圖片高度,然后包裝下再給小程序使用辈赋。

  • 使用request進(jìn)行請(qǐng)求
  • 使用image-size獲取圖片的高度
  • 最后將獲取后將數(shù)據(jù)寫(xiě)入文件鲫忍,啟動(dòng)一個(gè)服務(wù)提供接口

這里主要說(shuō)下碰到的問(wèn)題

1、request模塊的請(qǐng)求默認(rèn)返回來(lái)的是個(gè)String類(lèi)型的字符串钥屈,使用image-size模塊傳入的必須是Buffer悟民,怎么破呢?在request請(qǐng)求中設(shè)置encodingnull即可

2篷就、我們這里爬取了100張圖片射亏,怎么保證都已經(jīng)爬取完了呢?可以這樣寫(xiě)

Promise.all(List.map(item => getImgData(item)))  // getImgData函數(shù)是獲取圖片的函數(shù) 會(huì)返回個(gè)promise

3竭业、如果請(qǐng)求了幾次智润,發(fā)現(xiàn)有的圖片獲取不到了,報(bào)錯(cuò)了永品,怎么回事呢做鹰,人家畢竟做了防爬的,恭喜你中獎(jiǎng)了鼎姐,換個(gè)ip再試吧(可以把代碼放在服務(wù)器上面,或者換個(gè)Wi-Fi)更振,其實(shí)我們只需要爬一次就行炕桨,生成完文件還爬干嘛啊。

完整代碼請(qǐng)戳github

我們回到小程序肯腕,此時(shí)接口返回的數(shù)據(jù)如下

image

可以看到每個(gè)圖片都有高度了献宫,接下來(lái)我們實(shí)現(xiàn)瀑布流布局,等下实撒,我們搞下瀑布流布局的懶加載姊途,關(guān)于小程序的懶加載涉瘾,猛戳了解更多

怎么實(shí)現(xiàn)呢捷兰?主要分為兩步

1立叛、將元素瀑布流布局

2、創(chuàng)建IntersectionObserver贡茅,進(jìn)行懶加載

先開(kāi)始我們的布局吧

wxml

<view class='container'>
  <view class='pic pic-{{index}}' wx:for="{{list}}" style="height:{{item.height}}px;left:{{item.left}}; top:{{item.top}}; position:{{item.position}}" wx:key="{{item.index}}">
    <image src='{{item.url}}' wx:if="{{item.show}}"></image>
    <view class='default' wx:if="{{!item.show}}"></view>
  </view>
</view>

上面我們使用wx-if通過(guò)show這個(gè)字段來(lái)進(jìn)行判斷了圖片是否加載秘蛇,

使用一個(gè)view組件用來(lái)占位,然后更改show字段就可以顯示圖片了

js

我們使用兩個(gè)for循環(huán)顶考,先來(lái)進(jìn)行布局

let cols = 2
    let list = this.data.list
    let heightArr = [];


    for(let i in list){
      var boxHeight = list[i].height
      if (i < cols) {
        heightArr.push(boxHeight + 5)
      } else {
        var minBoxHeight = Math.min.apply(null, heightArr);
        var minBoxIndex = getMinBoxIndex(minBoxHeight, heightArr);
        list[i].position = 'absolute'
        list[i].top = `${minBoxHeight}px`
        list[i].left = minBoxIndex * 182 + 'px'
        list[i].left = minBoxIndex == 0 ? minBoxIndex * 182 + 'px' : minBoxIndex * 182 + 4 + 'px'
        heightArr[minBoxIndex] += (boxHeight + 5)
      }
    }
    this.setData({
      list
    })

布局完后赁还,創(chuàng)建IntersectionObserver,動(dòng)態(tài)判斷image節(jié)點(diǎn)的顯示

for (let i in list) {
      
    wx.createIntersectionObserver().relativeToViewport({ bottom: 20 }).observe('.pic-' + i, (ret) => {
        if (ret.intersectionRatio > 0) {
          list[i].show = true
        }
        this.setData({
          list
        })
 })
}

最后

我們使用三種方式完成了小程序的瀑布流布局驹沿,還額外完成了基于瀑布流的懶加載艘策。可以發(fā)現(xiàn)使用css最簡(jiǎn)便渊季,雖然小程序不能操作DOM柬焕,但是我們改完數(shù)據(jù)其實(shí)和改變DOM一樣,將觀念轉(zhuǎn)變過(guò)來(lái)梭域,小程序的開(kāi)發(fā)還是很爽的斑举。

最后的最后,各位病涨,周末快樂(lè)富玷。

github

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市既穆,隨后出現(xiàn)的幾起案子赎懦,更是在濱河造成了極大的恐慌,老刑警劉巖幻工,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件励两,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡囊颅,警方通過(guò)查閱死者的電腦和手機(jī)当悔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)踢代,“玉大人盲憎,你說(shuō)我怎么就攤上這事「炜妫” “怎么了饼疙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)慕爬。 經(jīng)常有香客問(wèn)我窑眯,道長(zhǎng)屏积,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任磅甩,我火速辦了婚禮炊林,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘更胖。我一直安慰自己铛铁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布却妨。 她就那樣靜靜地躺著饵逐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪彪标。 梳的紋絲不亂的頭發(fā)上倍权,一...
    開(kāi)封第一講書(shū)人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音捞烟,去河邊找鬼薄声。 笑死,一個(gè)胖子當(dāng)著我的面吹牛题画,可吹牛的內(nèi)容都是我干的默辨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼苍息,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼缩幸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起竞思,我...
    開(kāi)封第一講書(shū)人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤表谊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后盖喷,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體爆办,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年课梳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了距辆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惦界,死狀恐怖挑格,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沾歪,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布雾消,位于F島的核電站灾搏,受9級(jí)特大地震影響挫望,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狂窑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一媳板、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泉哈,春花似錦蛉幸、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至烫沙,卻和暖如春匹层,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锌蓄。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工升筏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瘸爽。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓您访,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親剪决。 傳聞我的和親對(duì)象是個(gè)殘疾皇子灵汪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 問(wèn)答題47 /72 常見(jiàn)瀏覽器兼容性問(wèn)題與解決方案? 參考答案 (1)瀏覽器兼容問(wèn)題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,737評(píng)論 1 92
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,761評(píng)論 25 707
  • 春天喚醒沉睡的大地 嫩綠的芽?jī)禾匠隽祟^ 它睜開(kāi)懵懂的眼睛 看見(jiàn)了一個(gè)美麗的姑娘 從它的枝條下走過(guò) 長(zhǎng)長(zhǎng)的頭發(fā)窈窕的...
    愛(ài)寶丫閱讀 283評(píng)論 6 6
  • 那年長(zhǎng)街春意正濃也就一個(gè)轉(zhuǎn)身你醉眼迷夢(mèng)跌入懷袖也就一個(gè)眼神注定癡纏一生 你曾是誰(shuí)的夢(mèng)夢(mèng)醒過(guò)后注定不是彼此的世界后來(lái)...
    小橋君子閱讀 657評(píng)論 0 2
  • 理想今年你幾歲 你總是誘惑著年輕的朋友
    方圓日記閱讀 553評(píng)論 1 2