我的需求:html
// 瀑布流佈局 waterFall() { ... const columns = 3 // 列數 const gap = 10; // 間隔 const itemWidth = ~~((this.getClient().width / columns - gap)) ... }, // 獲取頁面寬度 getClient() { const SCROLL_WIDTH = 20 return { /* ** window.innerWidth - SCROLL_WIDTH: 頁面寬度(包括滾動條)- 比滾動條多一點的寬度 ** 不使用document.body.clientWidth 和 document.documentElement.clientWidth緣由: ** 頁面首次加載時的寬度沒有被滾動條擠壓的,這樣會致使滾動加載時獲取的頁面寬度與首次的寬度不一致 ** 通常瀏覽器滾動條寬度爲17px,減去20是保守估計併爲右側留必定空間 ** 移動端滾動條是懸浮在頁面在上,不會形成擠壓,所以在移動端中能夠不減去滾動條寬度 */ width: window.innerWidth - SCROLL_WIDTH // width: window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth } }, // 對box進行佈局 reflow(el, itemWidth, columns, gap) { el.style.width = itemWidth + 'px' ... }
// 瀑布流佈局 waterFall() { const columns = 3 // 列數 const gap = 10; // 間隔 const itemWidth = ~~((this.getClient().width / columns - gap)) const box = document.getElementById("big-box") const items = box.children for (let i = this.loadCount; i < items.length; i++, this.loadCount++) { // 獲取圖片元素 const img = items[i].getElementsByTagName('img')[0] // 圖片有緩存時直接佈局(主要在窗口尺寸變化時調用) if(img.complete) { this.reflow(items[i], itemWidth, columns, gap) } // 圖片無緩存時先對加載速度快的圖片進行佈局 else { img.onload = () => { this.reflow(items[i], itemWidth, columns, gap) } } } }, // 對box進行佈局 reflow(el, itemWidth, columns, gap) { el.style.width = itemWidth + 'px' // 第一行 if (this.arr.length < columns) { el.style.top = 0; el.style.left = (itemWidth + gap) * this.arr.length + 'px' this.arr.push(el.offsetHeight) } // 其餘行 else { // 最小的列高度 const minHeight = Math.min(...this.arr) // 當前高度最小的列下標 const index = this.arr.indexOf(minHeight) el.style.top = minHeight + gap + 'px' el.style.left = (itemWidth + gap) * index + 'px' this.arr[index] = this.arr[index] + el.offsetHeight + gap } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <title>JS 實現瀑布流(Vue)</title> <style> .container { position: relative; } .box { position: absolute; width: 0; /* 設置0是爲了佈局時才顯示圖片,防止看到圖片都在第一張的位置上堆疊 */ } .box img { width: 100%; } </style> </head> <body> <div id="app"> <div id="big-box" class="container"> <div class="box" v-for="(item, index) in pic_list" :key="index"> <img :src="item"> </div> </div> </div> <script> new Vue({ el: '#app', name: 'WaterFall', data() { return { isReSize: false, // 窗口尺寸是否發生變化 lock: true, // 鎖 pic_list: [], arr: [], // 存放每一列的最小高度 loadCount: 0 // 已經佈局好的元素下標 } }, methods: { // 瀑布流佈局 waterFall() { const columns = 3 // 列數 const gap = 10; // 間隔 const itemWidth = ~~((this.getClient().width / columns - gap)) // console.log(this.getClient().width, itemWidth) const box = document.getElementById("big-box") const items = box.children // console.log(items) // 窗口尺寸發生變化時,所有box從新佈局 if(this.isReSize) { this.loadCount = 0 this.arr = [] this.isReSize = false } for (let i = this.loadCount; i < items.length; i++, this.loadCount++) { // 獲取圖片元素 const img = items[i].getElementsByTagName('img')[0] // 圖片有緩存時直接佈局(主要在窗口尺寸變化時調用) if(img.complete) { this.reflow(items[i], itemWidth, columns, gap) } // 圖片無緩存時先對加載速度快的圖片進行佈局 else { img.onload = () => { this.reflow(items[i], itemWidth, columns, gap) } } } console.log('-------------------------------') }, // 對box進行佈局 reflow(el, itemWidth, columns, gap) { el.style.width = itemWidth + 'px' // 第一行 if (this.arr.length < columns) { el.style.top = 0; el.style.left = (itemWidth + gap) * this.arr.length + 'px' this.arr.push(el.offsetHeight) } // 其餘行 else { // 最小的列高度 const minHeight = Math.min(...this.arr) // 當前高度最小的列下標 const index = this.arr.indexOf(minHeight) // console.log(index, minHeight) el.style.top = minHeight + gap + 'px' el.style.left = (itemWidth + gap) * index + 'px' this.arr[index] = this.arr[index] + el.offsetHeight + gap } // console.log(JSON.parse(JSON.stringify(this.arr))) }, // 獲取頁面寬度 getClient() { const SCROLL_WIDTH = 20 return { /* ** window.innerWidth - SCROLL_WIDTH: 頁面寬度(包括滾動條)- 比滾動條多一點的寬度 ** 不使用document.body.clientWidth 和 document.documentElement.clientWidth緣由: ** 頁面首次加載時的寬度沒有被滾動條擠壓的,這樣會致使滾動加載時獲取的頁面寬度與首次的寬度不一致 ** 通常瀏覽器滾動條寬度爲17px,減去20是保守估計併爲右側留必定空間 ** 移動端滾動條是懸浮在頁面在上,不會形成擠壓,所以在移動端中能夠不減去滾動條寬度 */ width: window.innerWidth - SCROLL_WIDTH // width: window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth } }, // 延遲函數,防止短期內執行屢次 wait(func, time=300) { if(this.lock) { this.lock = false setTimeout(() => { func() this.lock = true }, time) } }, async getPic() { // const {data: res} = await axios.get('http://127.0.0.1:5000/pics') // 經過訪問本地資源模擬獲取圖片路徑 res = [ "/static/img/1.jpg", "/static/img/2.jpg", "/static/img/3.jpg", "/static/img/4.jpg", "/static/img/5.jpg", "/static/img/6.jpg", "/static/img/7.png", "/static/img/8.png", "/static/img/9.jpg", "/static/img/10.jpg", "/static/img/11.jpg", "/static/img/12.jpg", "/static/img/13.jpg", "/static/img/14.jpg", "/static/img/15.jpg", "/static/img/16.jpg", "/static/img/17.png", "/static/img/18.jpg", "/static/img/19.jpg", "/static/img/20.png", ] this.pic_list.push(...res) await this.$nextTick() this.waterFall() } }, mounted() { this.getPic() // 窗口尺寸變化事件 window.onresize = () => { this.isReSize = true this.wait(this.waterFall) } // 窗口滾動事件 window.onscroll = () => { this.wait(() => { // 是否滾動到底部 const IS_BOTTOM = document.documentElement.scrollHeight - document.documentElement.scrollTop <= document.documentElement.clientHeight if(IS_BOTTOM) { this.getPic() console.log('到底了') } }) } } }) </script> </body> </html>