做一个小项目,需要瀑布流,就选他了,先看看效果
使用瀑布流布局组件: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}}
组件的使用:
<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组件
<!-- —————————————↓SCSS———————分界线————————————————————————— --> <style lang="less"> .vue-waterfall-easy { position: relative; 100%; // 移动端生效 .img-box { display: inline-block; 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 { 100%; background: yellow; } img { 100%; vertical-align: bottom; } .img-info { background: #fff; // padding: .6em; } } } .loading { text-align: center; 100%; position: fixed; bottom: 10px; left: 50%; margin-left: -15px; 30px; height: 30px; } .loading.first-loading { // 首次预加载白屏,让加载动画处于中间 top: 50%; margin-top: -15px; } .double-bounce1, .double-bounce2 { 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? '':{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', isMobile ? '' : colWidth+'px'}" ) .img-inner-box //- div.img-wraper(:style="{imgWidthC+'px',height:v.height?v.height+'px':''}") a.img-wraper( :style="{'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>