要实现动态瀑布流布局的图片墙:要做到两点:
一.实现瀑布流的图片墙,
二.使用函数节流监听window的resize事件
首先:实现瀑布流的图片墙,网上有各种各样式的实现,但是纯css的实现,总是有些bug,不容易控制,所以我使用js进行控制。
它的原理是:首先使用浮动布局,图片可浮动。具体结构:
这时除了第一行,其他的图片的都有较大的间距。剩下的工作需要js来完成:
①声明变量保存图片墙容器的dom object,声明变量保存图片墙每个图片容器的dom object,声明变量保存第一行图片容器的属性
②计算每行图片的数目,第一行每个图片容器的左边的距离,以及第一行每张图片容器的高度(因为除第一行外都是使用的绝对定位)
③开始计算剩余图片容器的位置,(主要包括图片容器的left和top,每设置一个图片容器的位置,那么就更新第一行图片容器数组的top值,因为判断下张图片容器的位置就是找上一行高度最小的图片容器,在它的下方放置);
**注意:
因为是动态的,所以当窗口变动时,第一行的图片容器数量会改变,所以原来不在第一行之列的元素会进入到第一行,但是此时它们依然是绝对定位,所以要清除绝对定位。
另外一点,如果你的html的font-size使用函数节流,此时瀑布流布局的图片墙也要用,要注意this的指向,我因为使用了面向对象,所以瀑布流布局的图片墙使用节流时要指向我的对象。**
**
完整代码:
<template>
<ul class="photos-wall clear" ref="photosWall">
<li class="photo" ref="photo" v-for="img in images" :key="img.index"><img :src="img.src" alt="瀑布流图片" /></li>
</ul>
</template>
<script type="text/javascript">
import Base from '../assets/js/base.js'
export default {
data() {
return {
images: [],
waterfullBoxObj: null,
waterfullElemObjs: null
}
},
created() {
this.init();
},
mounted() {
this.initGetDomObj();
},
methods: {
init: function () { // 一切数据和函数的入口
this.initBaseDatas();
},
initBaseDatas: function () { //初始化data中的所有数据
this.initImagesData()
},
initImagesData: function () { //初始化data中images属性的初始值
let images = [
{'index': 1, 'src': '../../static/imgs/waterfull/hy1.jpeg'},
{'index': 2, 'src': '../../static/imgs/waterfull/hy2.jpg'},
{'index': 3, 'src': '../../static/imgs/waterfull/hy3.jpg'},
{'index': 4, 'src': '../../static/imgs/waterfull/hy4.jpg'},
{'index': 5, 'src': '../../static/imgs/waterfull/hy5.jpg'},
{'index': 6, 'src': '../../static/imgs/waterfull/hy6.jpg'},
{'index': 7, 'src': '../../static/imgs/waterfull/hy7.jpg'},
{'index': 8, 'src': '../../static/imgs/waterfull/hy8.jpg'},
{'index': 9, 'src': '../../static/imgs/waterfull/hy9.jpg'},
{'index': 10, 'src': '../../static/imgs/waterfull/hy10.jpg'}
];
this.images = images;
},
initGetDomObj: function () { //初始化获取的dom元素的对象
let self = this;
// 延迟获取dom元素,因为图片未加载完毕,获取的元素的高度不正确
let timer = setTimeout(function () {
self.waterfullBoxObj = self.$refs.photosWall; //获取瀑布流容器对象
self.waterfullElemObjs = self.$refs.photo; //获取瀑布流容器中元素对象
let waterfullPhotos = ProxySingletonWaterfullPhotos(self.waterfullBoxObj, self.waterfullElemObjs);
window.addEventListener('resize', Base.throttle(waterfullPhotos.init, 200, waterfullPhotos), false);
}, 200);
}
}
}
let isDOM = (typeof HTMLElement === 'object')?
function(obj){
return obj instanceof HTMLElement;
}:
function(obj){
return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';
};
/**
* 使用代理模式创建WaterfullPhotos类的单例模式
*/
var ProxySingletonWaterfullPhotos = (function () {
let instanse;
return function (box, items) {
if (!instanse) {
instanse = new WaterfullPhotos(box, items);
}
return instanse;
}
}());
/**
* 瀑布流布局的图片墙
* @param object box [description]
* @param items items [description]
*/
function WaterfullPhotos (box, items) {
// 检测参数
if (!box || !items) {
throw new Error('function WaterfullPhotos arguments can not missing!');
return ;
}
if (!isDOM(box)) {
throw new Error('function WaterfullPhotos first param must be a dom object!');
return ;
}
if (!Array.isArray(items) || (items.length <= 0)) {
throw new Error('function WaterfullPhotos second param must be a dom object array!');
return ;
}
for (let i = 0, len = items.length; i < len; i++) {
if (!isDOM(items[i])) {
throw new Error('function WaterfullPhotos second param must be a dom object array!');
return ;
}
}
this.box = box; //图片墙的容器
this.items = items; //图片墙的容器中的每张图片的盒子容器
this.firstColum = []; //图片墙的容器的第一行图片组
this.columnNum = 0; //图片墙的容器每行图片的数目
// 启动函数
this.init();
}
/**
* 获取瀑布流图片第一列的属性
* @return {[type]} [description]
*/
WaterfullPhotos.prototype.getWaterfullFirstColumn = function () {
// 计算每一列的数目(总宽度/每个元素的宽度[元素的宽度相等])
this.columnNum = Math.floor(this.box.offsetWidth / this.items[0].offsetWidth);
let waterfullElemObj_w = this.items[0].offsetWidth;
this.clearFirstColumnPhotosPosition();
let obj = {}, // 存放第一列元素的属性
_firstColum = []; //保存第一列元素
for (let i = 0; i < this.columnNum; i++) {
obj.width = waterfullElemObj_w;
obj.left = i * waterfullElemObj_w;
obj.top = this.items[i].offsetHeight;
_firstColum.push(obj);
obj = {};
}
this.firstColum = _firstColum;
obj = null;
_firstColum = null;
}
/**
* 清除第一行图片组中某些元素的绝对定位
* @return {[type]} [description]
*/
WaterfullPhotos.prototype.clearFirstColumnPhotosPosition = function () {
// 当窗口触发resize事件时,可能原来设置绝对为的元素加入到第一行,所以要清除绝对定位
for (let i = 0; i < this.columnNum; i++) {
if (this.items[i].style.position === 'absolute') {
this.items[i].style.position = '';
this.items[i].style.top = '';
this.items[i].style.left = '';
}
}
}
/**
* 设置图片墙除第一行图片其他图片的位置
* @param array arr 除第一行图片盒子的其他图片盒子组成数组
*/
WaterfullPhotos.prototype.setWaterfullOtherPhotoPosition = function (arr) {
for(let i = 0, item; item = arr[i++];) {
let minObj = this.getObjArrayAttributeMinValue(this.firstColum, 'top');
item.style.position = 'absolute';
item.style.top = this.firstColum[minObj.minIndex].top + 'px';
this.firstColum[minObj.minIndex].top += item.offsetHeight; //更新当列据顶部的高度
item.style.left = this.firstColum[minObj.minIndex].left + 'px';
}
}
/**
* 获取对象数组某个属性的最小值
* @param array arr 需要获取最小值和最小值位置的目标对象
* @param array attr 需要获取最小值和最小值位置的目标对象的属性
* @return object 获取的最小值和最小值位置
*/
WaterfullPhotos.prototype.getObjArrayAttributeMinValue = function (arr, attr) {
let _arr = [];
for (let i = 0, item; item = arr[i++];) {
if (item.hasOwnProperty(attr)) {
_arr.push(item[attr]);
}
}
let minValue = Math.min.apply(null, _arr),
minIndex = _arr.indexOf(minValue);
return {
minValue: minValue,
minIndex: minIndex
};
}
/**
* WaterfullPhotos类的启动函数
* @return {[type]} [description]
*/
WaterfullPhotos.prototype.init = function () {
this.getWaterfullFirstColumn();
this.setWaterfullOtherPhotoPosition(this.items.slice(this.firstColum.length));
}
</script>
<style lang="scss" scoped>
/*photos wall style*/
.photos-wall{
position: relative;
width: 100%;
min-height: 100%;
font-size: inherit;
.photo{
display: inline-block;
width: 15.625em;
min-width: 5.5em;
float: left;
font-size: inherit;
img{
width: 100%;
vertical-align: middle;
}
}
}
</style>
base.js
function isPC () {
// 判断用户的设备类型是否是PC端
let userAgentInfo = navigator.userAgent;
const AGENTS = ["Android", "iPhone",
"SymbianOS", "Windows Phone",
"iPad", "iPod"];
let flag = true;
for (var v = 0; v < AGENTS.length; v++) {
if (userAgentInfo.indexOf(AGENTS[v]) > 0) {
flag = false;
break;
}
}
return flag;
}
/*
用于屏幕的适配
*/
function adaptive () {
let htmlObj = document.querySelector('html') || document.getElementsByTagName('html')[0] || document.body;
const PC_BASE_SEIZE = 136.6,
MOBILE_BASE_SEIZE = 37.5;
try{
if (!htmlObj) {
throw new Error('Get the html element error!');
}
let client_w = htmlObj.clientWidth,
baseFontSize = client_w/PC_BASE_SEIZE;
if (!isPC()) {
baseFontSize = client_w/MOBILE_BASE_SEIZE;
}
htmlObj.style.fontSize = baseFontSize + 'px';
} catch (e) {
console.log('base.js/function '+ this.getName +' error:', e);
}
}
// 函数节流
let throttle = function (fn, interval, context) {
let _self = fn, //保存待执行函数的引用
firstTime = true, //是否是第一次调用
timer = null; //定时器
return function () {
let args = arguments, //保存参数
_me = context || this; //保存throttle函数的引用
//第一次执行不需要延迟执行
if (firstTime) {
_self.apply(_me, args);
return firstTime = false;
}
//如果timer存在说明函数上一次延期汗没有执行完毕
if (timer) {
return false;
}
timer = setTimeout(function () {
clearTimeout(timer);
timer = null;
_self.apply(_me, args);
}, interval || 400);
};
}
// 窗口尺寸改变触发的事件
function resize () {
window.addEventListener('resize', throttle(adaptive, 300), false);
}
module.exports = {
resize: resize,
isPC: isPC,
throttle: throttle,
init: function () {
adaptive();
}
};
/*# sourceMappingURL=base.js.map */