瀑布流佈局 不到30行代碼實現(JavaScript + absolute)支持懶加載

最後效果


@


前言

瀑布流 網站頁面佈局
瀑布流,又稱瀑布流式佈局。是比較流行的一種網站頁面佈局,視覺表現爲參差不齊的多欄佈局,隨着頁面滾動條向下滾動,這種佈局還會不斷加載數據塊並附加至當前尾部。最早採用此佈局的網站是Pinterest,逐漸在國內流行開來。國內大多數清新站基本爲這類風格。

簡單來說: 瀑布流式佈局其實式一種網頁的樣式效果,簡單來說就是一種單個寬度固定單個圖片高度根據內容適應多個塊自動換行,但是緊湊拼在一起的效果。既可以使用純css快速的達到這種樣式效果,也可以通過JavaScript輔助,實現更豐富合理的效果


一、使用css實現瀑布流佈局

1.flex 佈局

使用flex佈局,將容器設置爲flex佈局,設置主軸方向爲列方向,允許換行,容器設定固定高度(這樣纔會自動換列),然後子元素設置寬度爲對應比例,在某一列填滿時會自動往下一列填充,並且每一列元素填充十分緊湊****使用flex佈局實現的基礎的瀑布式佈局
優化方向:使用order屬性優化排序方向

2.column-count 多欄佈局

類似的還有使用 多欄佈局,使用column-count屬性,設置多欄佈局,效果跟flex佈局類似,排列方向也是從上到下,從左到右

3.grid 網格佈局

二、結合JavaScript的瀑布流佈局實現

1.推薦原因

瀑布流式佈局經常被使用在圖片的展示頁面,大量的圖片資源加載,如果只用css樣式的話必須一次性加載所有圖片,我們期待的是瀑布流懶加載結合在一起,在滾動視圖的同時,不斷加載下面的內容

2.實現步驟

如果需要結果的話可以直接跳到文章最後

a.初步實現:結合JavaScript實現瀑布流

原理:

  1. 子元素絕對定位
  2. 通過javascript設置偏移量

html代碼:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./index.css">
</head>

<body>
    <div id="app"></div>
    <!-- <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script> -->
    <!-- <script src="./src/vue.min.js"></script> -->
    <script src="./util.js"></script>
    <script src="./index.js"></script>
</body>

</html>

css 代碼:

html {
    background-color: black;
}

#app {
    position: relative;
    width: 100%;
    margin: auto;
}

.item {
    position: absolute;
    border-radius: 20px;
    border: 10px solid black;
    box-sizing: border-box;
    text-align: center;
    color: white;
}

utils.js 代碼:

const randomImgSize = [	// 這裏是模擬各種尺寸的圖片
    {
        width:1920,
        height:1080
    },{
        width:640,
        height:720
    },{
        width:720,
        height:680
    },{
        width:720,
        height:1920
    },{
        width:900,
        height:600
    },{
        width:400,
        height:300
    },{
        width:2048,
        height:1920
    },{
        width:700,
        height:900
    },{
        width:1600,
        height:900
    },{
        width:200,
        height:200
    }
]

function randomColor(){
    let r = Math.floor(Math.random() * 255)
    let g = Math.floor(Math.random() * 255)
    let b = Math.floor(Math.random() * 255)
    return `rgb(${r},${g},${b})`
}

index.js 代碼

   		// 創建元素函數
       function generateBlock(size,color){
           let dom = document.createElement('div')
           dom.setAttribute('class','item')
           dom.style.width = '240px'			// 這裏默認寫死寬度,後面會優化
           dom.style.height = `${240/size.width * size.height}px`		// 由於這裏沒有圖片資源,高度模擬計算
           // 圖片資源的話不需要直接width:100% 高度會自適應
           dom.style.backgroundColor = color
           // 顏色也是隨機生成而已
           document.getElementById('app').appendChild(dom)
           return dom
       }
       // 元素統計數組
       let domArr = []
       // 元素高度數組
       let heightArr = new Array(4)	// 創建對應列數,存儲每一列高度,用來設置偏移量
       heightArr.fill(0)
       // 常規不考慮高度不整齊邏輯
       randomImgSize.forEach(ele=>{
           let dom =generateBlock(ele,randomColor())
           domArr.push(dom)		// 通過transform 屬性可以一次設置,不用設置left和top,不過也一樣
           dom.style.transform =  `translate(${(domArr.length-1)%4 * 100}%, ${heightArr[domArr.length%4]}px)`
           heightArr[domArr.length%4] += dom.offsetHeight
       })

實現內容:

  1. 初步實現瀑布流佈局,正確計算每一個塊(圖片)的正確位置

  2. 每個塊只是死板的按照從左到右,從上到下的順序,可以優化:下一個塊生成位置爲當前列最低高度位置,自動填補進去

  3. 沒有實現懶加載

    基礎的js結合實現瀑布流

b.稍微優化:每次自動填補到高度最低

index.js

        // 創建元素函數
        function generateBlock(size,color){
            let dom = document.createElement('div')
            dom.setAttribute('class','item')
            dom.style.width = '240px'
            dom.style.height = `${240/size.width * size.height}px`
            dom.style.backgroundColor = color
            document.getElementById('app').appendChild(dom)
            return dom
        }
        // 元素統計數組
        // let domArr = []
        // 元素高度數組
        let heightArr = new Array(4)
        heightArr.fill(0)
        // 常規不考慮高度不整齊邏輯
        // randomImgSize.forEach(ele=>{
        //     let dom =generateBlock(ele,randomColor())
        //     domArr.push(dom)
        //     dom.style.transform =  `translate(${(domArr.length-1)%4 * 100}%, ${heightArr[domArr.length%4]}px)`
        //     heightArr[domArr.length%4] += dom.offsetHeight
        // })

        // 考慮高度不整齊邏輯
        randomImgSize.forEach(ele=>{
            let dom =generateBlock(ele,randomColor())
            let minHeight=  Math.min(...heightArr)
            let minHeightIndex = heightArr.indexOf(minHeight)
            dom.style.transform =  `translate(${minHeightIndex * 100}%, ${minHeight}px)`
            heightArr[minHeightIndex] += dom.offsetHeight
        })

實現效果:
優化高度填充對比

  1. 可以看到效果對比之前還是要好很多,不會出現極高或者極低的情況,而且代碼還簡化了,不需要數組來緩存元素,直接通過長度爲4 的高度數組,查詢應該插入到哪個列
  2. 對應列的偏移量:x爲 100% * 對應列下標,y爲對應列下標高度
  3. 但是寬度還是寫死的,而且沒有實現懶加載

b.最終結果 (不到三十行代碼)支持懶加載

index.js

		// 響應式
        const columns = 3
        let tempArr = new Array(columns)  // 創建數組用來緩存高度
        tempArr.fill(0)
        function create(){
            let observer = new IntersectionObserver(callback)
            function callback(entries){
                if(entries[0].isIntersecting){  // 如果已進入視圖,停止監聽,並且生成新的元素
                    observer.unobserve(dom)
                    create()
                }
            }
            function generateBlock(size,color){
                let dom = document.createElement('div')
                dom.setAttribute('class','item')
                dom.style.width = `${Math.floor(100/columns)}%`         // 寬度爲百分比
                dom.style.height = `${300/size.width * size.height}px`  // 300爲默認高度
                dom.style.backgroundColor = color
                document.getElementById('app').appendChild(dom)
                return dom
            }
            let donSizeIndex = Math.floor(Math.random() * randomImgSize.length) // 隨機生成大小(模擬不同尺寸的圖片)
            let dom =generateBlock(randomImgSize[donSizeIndex],randomColor())   // 生成隨機塊
            let minHeight=  Math.min(...tempArr)                                // 找到對應列最小高度的值
            let minHeightIndex = tempArr.indexOf(minHeight)                     // 找到對應列最小高度的下標
            dom.style.transform =  `translate(${minHeightIndex*100}%, ${minHeight}px)`      // 根據下標進行變換,變換寬度爲偏移多少個下標,上下爲該下標所有高度
            tempArr[minHeightIndex] += dom.offsetHeight         // 對應下標增加高度
            observer.observe(dom)   // 觀察該元素用作懶加載
        }
        create()

實現效果:
在這裏插入圖片描述
實現比較簡單其實,主要是javascript操作樣式,設置對應transform屬性,通過全局設置一個高度數組,每次添加元素時獲取每一列高度,選取最小高度列,將元素偏移量設置正常,並且通過intersectionObserver對象,監聽與視口交叉事件,每次觸發時,生成新的元素

同理可以拓展到加載圖片..等等


總結

以上就是今天要的結合JavaScript實現圖片瀑布流式佈局+懶加載的實現過程,本文僅僅簡單介紹了瀑布流佈局相關設計思路,如果需要增加其他功能可以在該demo的基礎上進行修改

覺得有幫助的話,點贊關注支持一下,b站前端同名分享號:猿油站

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章