作一個小項目,須要瀑布流,就選他了,先看看效果vue
使用瀑布流佈局組件:vue-waterfall-easy 下載引入: 方式一:直接從git上覆制組件的完整代碼,引入vue組件文件便可 import vueWaterfallEasy from '你的路徑/組件名.vue' 方式二:經過npm全局安裝:cnpm install vue-waterfall-easy --save-dev import vueWaterfallEasy from 'vue-waterfall-easy' 報錯注意: Cannot find module 'pug' 緣由是:沒有安裝pug模塊,安裝:cnpm install --save pug 或去除:lang='pug' sass-loader沒安裝: 解決:安裝sass或修改sass爲less 註冊: 要在當前組件中註冊該組件:export default { components:{vueWaterfallEasy}}
組件的使用:ios
<template> <div id="all_user"> <div class="search_box"> <input type="text" placeholder="請輸入編號或名稱"><button><i class="tt tt-quanburen"></i>搜索</button> </div> <vueWaterfallEasy :imgsArr="imgsArr" @scrollLoadImg="fetchImgsData"> <template slot-scope="props"> <div class="player_info"> <div class="title"><i class="tt tt-quanburen"></i>{{props.value.info}}</div> <div class="ticket"> <mt-button @click="upLoadTicket(props.value.id)" size="small"><i class="tt tt-quanburen"></i>投票</mt-button> </div> <p class="num">{{props.index+1}}票</p> </div> </template> </vueWaterfallEasy> </div> </template> <script> import vueWaterfallEasy from './Waterfall/vue-waterfall-easy.vue' export default { name: 'app', data() { return { imgsArr: [], fetchImgsArr: [] } }, components: { vueWaterfallEasy }, methods: { // 假數據 initImgsArr(n, m) { //num 圖片數量 var arr = [] for (var i = n; i < m; i++) { arr.push({ id:i,src: `./src/assets/images_test/${i + 1}.jpg`, link: 'https://www.baidu.com', info: '一些圖片描述文字' }) } return arr }, fetchImgsData() { this.imgsArr = this.imgsArr.concat(this.fetchImgsArr) }, upLoadTicket(index){ //投票按鈕 console.log(index); } }, created() { this.imgsArr = this.initImgsArr(0, 5) this.fetchImgsArr = this.initImgsArr(5, 10) // 模擬每次請求的新的圖片的數據數據 }, } </script>
vue-waterfall-easy.vue組件git
<!-- —————————————↓SCSS———————分界線————————————————————————— --> <style lang="less"> .vue-waterfall-easy { position: relative; width: 100%; // 移動端生效 .img-box { display: inline-block; width: 50%; // 移動端生效 box-sizing: border-box; float: left; // 首行排版 transition: left 1s, top 1s; .img-inner-box { box-shadow: 0 1px 3px rgba(0, 0, 0, .3); .img-wraper { width: 100%; background: yellow; } img { width: 100%; vertical-align: bottom; } .img-info { background: #fff; // padding: .6em; } } } .loading { text-align: center; width: 100%; position: fixed; bottom: 10px; left: 50%; margin-left: -15px; width: 30px; height: 30px; } .loading.first-loading { // 首次預加載白屏,讓加載動畫處於中間 top: 50%; margin-top: -15px; } .double-bounce1, .double-bounce2 { width: 100%; height: 100%; border-radius: 50%; background-color: #67CF22; opacity: 0.6; position: absolute; top: 0; left: 0; animation: bounce 2.0s infinite ease-in-out; } .double-bounce2 { animation-delay: -1.0s; } @keyframes bounce { 0%, 100% { transform: scale(0.0); } 50% { transform: scale(1.0); } } } </style> <!-- —————————————↓HTML————————分界線———————————————————————— --> <template lang="pug"> .vue-waterfall-easy( :style="isMobile? '':{width:colWidth*columnCount+'px',left:'50%',marginLeft: -1*colWidth*columnCount/2 +'px'}" ) div.img-box( v-for="(v,i) in imgsArrC", :style="{padding:gap/2+'px',width: isMobile ? '' : colWidth+'px'}" ) .img-inner-box //- div.img-wraper(:style="{width:imgWidthC+'px',height:v.height?v.height+'px':''}") a.img-wraper( :style="{width:'100%',height:v.height?'auto':''}" :href="v.link" ) img(:src="v.src") div.img-info slot(:index="i",:value="v") .loading(v-if="isPreloadingC",:class="{'first-loading':isFirstTIme}") div.double-bounce1 div.double-bounce2 </template> <!-- ——————————————↓JS—————————分界線———————————————————————— --> <script> //import XXX from './components/XXX' export default { name: 'vue-waterfall-easy', //組件參數 props: { gap: { //圖片間隔 type: Number, default: 10 }, maxCols: { //最大的列數 type: Number, default: 5 }, imgsArr: { //請求返回的圖片數據 type: Array, required: true }, imgWidth: { //制定圖片的同一寬度 type: Number, default: 240 }, timeOut: { // 預加載事件小於500毫秒就不顯示加載動畫,增長用戶體驗 type: Number, default: 500 } }, data() { return { msg: 'this is from vue-waterfall-easy.vue', columnCount: NaN, // 列數,根據窗口大小初始化 isMobile: navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i), // 初始化移動端 beginIndex: NaN, // 第二列首張圖片的index,從這一張開始從新計算圖片位置 colsHeightArr: [], // 每一列的圖片總和高度爲元素組成的數組 imgBoxEls: null, // 全部的.img-box元素 isPreloading: true, // 預加載狀態中(1.以等待圖片替換 2.圖片所有預加載完顯示) isPreloadingC: true, imgsArrC: [], // 預加載完以後再纔開始 loadedCount: 0, // 已經加載圖片數量 isFirstTIme: true, // 首次加載 } }, computed: { colWidth() { // 每一列的寬度 return this.imgWidth + this.gap }, imgWidthC() { // 對於移動端從新計算圖片寬度 return this.isMobile ? window.innerWidth / 2 - this.gap*2 : this.imgWidth } }, methods: { waterfall() { // 執行瀑布佈局 for (var i = this.beginIndex; i < this.imgsArr.length; i++) { var minHeight = Math.min.apply(null, this.colsHeightArr) // 最低高低 var minIndex = this.colsHeightArr.indexOf(minHeight) // 最低高度的索引 var width = this.imgBoxEls[0].offsetWidth // 圖片的寬度獲取 // 設置元素定位的位置 this.imgBoxEls[i].style.position = 'absolute' this.imgBoxEls[i].style.left = minIndex * width + 'px' this.imgBoxEls[i].style.top = minHeight + 'px' // 更新colsHeightArr this.$set(this.colsHeightArr, minIndex, minHeight + this.imgBoxEls[i].offsetHeight) } this.beginIndex = this.imgsArr.length }, loadFn(e, oImg, i) { // 每張圖片預加載完成執行函數 this.loadedCount++ if (e.type === 'load') { // 使用圖片原始寬度計算圖片的高度 this.$set(this.imgsArr[i], 'height', Math.round(this.imgWidthC / (oImg.width / oImg.height))) } if (this.loadedCount === this.imgsArr.length) { this.imgsArrC = this.imgsArr.concat([]) this.isPreloading = false this.isFirstTIme = false // 預加載完畢 this.$nextTick(() => { this.initImgBoxEls() this.$emit('preloaded') }) } }, preload() { this.imgsArr.forEach((v, i) => { if (i < this.loadedCount) return var oImg = new Image() oImg.addEventListener('load', (e) => { this.loadFn(e, oImg, i) }) oImg.src = v.src }) }, // -----------------初始化化------------------------ initColsHeightArr() { // 第一行元素的高度組成的數組-初始化 this.colsHeightArr = [] // 列數發生變化從新初始化 for (var i = 0; i < this.columnCount; i++) { this.imgBoxEls[i].style.position = 'static' // 重置下position var height = this.imgBoxEls[i].offsetHeight this.colsHeightArr.push(height) } }, initImgBoxEls() { // 初始化全部裝圖片的元素集合,注意高度獲取須要在圖片加載完成以後,因此在window.onload 事件中初始化 this.imgBoxEls = document.getElementsByClassName('img-box') }, initColumnCount() { // 列數初始化 var winWidth = window.innerWidth var columnCount = parseInt(winWidth / this.colWidth) columnCount = columnCount === 0 ? 1 : columnCount this.columnCount = this.isMobile ? 2 : (columnCount > this.maxCols ? this.maxCols : columnCount) }, }, mounted() { // ==1== 根據窗口大小初始化列數 this.initColumnCount() this.beginIndex = this.columnCount // 開始排列的元素索引 // ==2== 根據預加載完成的圖片的長寬比,計算圖片的高度 this.preload() this.$on('preloaded', () => { if (this.colsHeightArr.length === 0) this.initColsHeightArr() // 第一次初始化 this.waterfall() }) window.addEventListener('resize', () => { var old = this.columnCount this.initColumnCount() if (old === this.columnCount) return // 列數不變直接退出 this.beginIndex = this.columnCount // 開始排列的元素索引 this.initColsHeightArr() this.waterfall() }) // console.log(this.$el.parentNode) // console.log(this.$el.parentNode, this.$el.parentNode.scrollTop + this.$el.parentNode.offsetHeight, this.$el.parentNode.scrollHeight) this.$el.parentNode.addEventListener('scroll', () => { if (this.isPreloading) return const lastImgHeight = this.imgsArr[this.imgsArr.length - 1].height // console.log(this.$el.parentNode, this.$el.parentNode.scrollTop + this.$el.parentNode.offsetHeight, this.$el.parentNode.scrollHeight) if (this.$el.parentNode.scrollTop + this.$el.parentNode.offsetHeight > this.$el.parentNode.scrollHeight - lastImgHeight) { this.$emit('scrollLoadImg') console.log('加載'); } }) }, watch: { imgsArr(newV, oldV) { if (newV.length === oldV.length) return this.isPreloading = true // 預加載新的圖片資源 this.preload() // setTimeout(()=>{ // 模擬圖片預加載時間爲1s // this.preload() // },1000) }, isPreloading(v) { if (v) { setTimeout(() => { if (!this.isPreloading) return // 500毫秒內預加載完圖片則不顯示加載動畫 this.isPreloadingC = true }, this.timeOut) } else { this.isPreloadingC = false } } } } </script>