做一个小项目,需要瀑布流,就选他了,先看看效果
使用瀑布流布局组件: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;
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>