因为专题制作需要用到瀑布流,所以这阵子总结了几种实现瀑布流的方式。
纯css实现瀑布流主要有3种方式:
1. 多列布局multi-columns
2.Flexbox布局
3. grid布局
这是columns的语法:http://www.webhek.com/post/css3-multi-columns.html
html代码
html结构非常简单,id:box元素是瀑布流的容器,我们的目的是让它的子元素呈现瀑布流排列。这里面可以放置n个项。 css代码 然后在子元素中设定break-inside,这是为了避免子元素内部的文本块分解成单独的列。 我比较喜欢子容器刚好占满父容器,然后通过padding和margin来设置间距。 接下来就可以在item中填充图片和文字。 multi-columns实现瀑布流非常简单,能兼容IE10及以上。 但是只能够一列一列的排列。 和multi-columns相比,flexbox可以说是广为人知了。 html代码 flexbox瀑布流需要2层包裹item。 css代码 通过设置2层容器,分别设定为行flex-direction:row(默认)和列flex-direction:column。 横向flex布局嵌套多列纵向flex布局, 然后在往子容器中添加内容模块。 这种方式比起multi-columns来说,更麻烦,并且无法自动堆叠。 grid布局语法:https://www.html.cn/archives/8510 grid正在得到众多浏览器的支持,并且比flexbox更强大,我觉得很有学习的必要。 相比前面2个只能通过列排列的瀑布流来说,grid布局则可以实现横向排列的瀑布流。 html代码 css代码 我们需要通过 通过总结css3的三种瀑布流实现方式,我发现,他们虽然能够实现瀑布流布局,但却不符合我的项目需求。 需求:一次加载10张,滑动到图片底部,再加载10张。说白了就是移动端横向瀑布流配合无限滚动。 这是最常见的瀑布流使用方式了。 可惜是multi-columns通过列排列的,它会造成图片的乱序(因为我是下滑加载)。 所以,配合javascript的瀑布流还是不可或缺。 原理: 1.将一个容器分为n列。 2.判断这些列高度最低的一列,往这个列添加列表项 3.通过循环,重复添加,直到数据添加完毕。 html代码 html主要分成3层,id:waterfall为瀑布流总容器,class:column是它的子元素,将总容器划分为2列。 接下来可以直接在class:column中放置列表项了。 不过因为要计算每一列的内容高度(我采用flexbox,所以class:column元素高度充满父容器), 我在class:column里面又添加了一层容器,用来放置列表项。这是为了计算列表项的总高度。 css代码 css代码非常简单,设置容器的宽度,高度通过内容来撑开。 接下来是js,任务是动态的往高度最低的列添加列表项。 js代码 js代码的目的: 1.判断高度最低的列 2.往这一列添加列表项 但在实际过程中,因为是通过图片高度来撑开父级,也就是class:item1和class:item2。 而图片是异步加载的,所以要获取最低高度,我们必须在图片加载完毕后再进行高度获取。 所以我采用的方法是结合promise(promise可以看阮一峰的《es6入门标准》)和 图片onload事件(onload 事件在图片加载完成后立即执行,是异步回调)。 1.创建img图片对象,并添加src属性。 2.img.onload事件函数内执行res(),这可以保证在图片加载完毕后才执行then回调函数中的内容。 3.在then回调函数中,判断内容高度,往高度最低的一列中添加列表项。 4.通过循环载入全部数据。 这样就可以实现js瀑布流了。 但是实际上这还存在2个小问题。 1.图片加载并不是一张加载完毕后再加载另一张的。 如果前面的图片很大,也许后面的图片会先加载完毕。 这就会造成图片的乱序,(只会造成同一批加载的图片位置乱序)。 虽然造成的影响不是很大,但还是不够完美。 这可以采用图片预加载来完美解决。 第二个问题是不够完善,如果图片加载失败显示默认图片。 js代码 图片预加载有多种方式,我是通过Promise.all来做的。 Promise.all可以将多个promise实例包装成一个新的promise,并且当所有promise状态完成fulfilled,这个新的promise状态也为完成。 promise的状态为fulfilled才会执行then回调函数。 所以我们只要在那些promise中加载图片,当图片加载完毕后再修改状态fulfilled。 然后在新的promise中处理数据,就能实现图片预加载。 同时,我们可以监听onerror事件,当图片加载失败,显示为默认图片,再修改promise状态。 这样就可以实现一个还算ok的js瀑布流了。 还有一种方案,图片的懒加载,需要后台发送图片的宽高。 下面是效果,人的那张图片是加载失败的默认图片。 接下来是vue怎么实现瀑布流。 因为我的专题是用vue做的,其实和js原理都是一样的,就是判断最低高度,添加列表项。 但在实际做的时候,有一个需要注意的地方。 html代码 html代码和js的一样,只是绑定上数据。 css代码 js代码 vue和js的原理都是一致的,我们只需要控制2个数组的值。 唯一需要注意的是,vue.$nextTick函数,在官方文档中的解释:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。 因为我们需要获取列表项的高度,调用这个函数可以让我们获取到DOM更新后的高度。 但是不能直接循环获取,因为我们需要每添加一个列表项,就马上获取高度进行比较,然后再添加另一个列表项。 如果直接在vue.$nextTick函数中循环数据,那得到的高度都是一致的。 所以我用了递归的方式,每添加一个列表项,就调用vue.$nextTick函数来获取高度和添加列表项。 感觉瀑布流布局还是离不开js,因为往往要添加无限滚动功能,这些是运用css的瀑布流无法解决的。
div>
div>
div>
#box {
width: 400px;
column-width: 50%; /*设定列宽*/
column-count: 2; /*列数*/
column-gap: 0; /*列间距*/
}
.item {
break-inside: avoid; /*避免在元素内部断行并产生新列*/
}
你只要在父级元素中设定列数column-count,列间距column-gap,列宽column-width,子元素就会呈现瀑布流排列。demo:https://caizhichen.github.io/waterfall/column.html
flexbox
div>
div>
div>
div>
div>
#box {
width: 600px;
display: flex;
background: red;
}
.column {
width: 25%;
display: flex;
flex-direction: column;
}
.column div {
width: 100%;
}
grid
div>
#box {
display: grid;
grid-gap: 40px; /*复合属性,行和列间距*/
grid-template-columns: repeat(3, 1fr);/*设定3列,每个列高度相同*/
grid-auto-rows: minmax(50px, auto);/*为网格中的行设置默认大小*/
}
.item:nth-of-type(1) {
grid-row: 1 / 4; /* 复合属性,高度起始行 / 高度结束行 */
grid-column: 1; /* 该列中的第1行 */
}
.item:nth-of-type(2) {
grid-row: 1 / 3;
grid-column: 2;
}
.item:nth-of-type(3) {
grid-row: 1 / 4;
grid-column: 3;
}
/* 其他项,逐个添加 */
grid-row
和grid-column
来指定每个子元素所在的区域。demo:https://caizhichen.github.io/waterfall/grid.html
javascript
div>
div>
div>
div>
div>
#waterfall {
width: 400px; /*设定总容器宽度*/
display: flex;
}
.column {
flex: 1; /*每个子元素各占一份*/
}
.column img {
width: 200px;
display: block;/*避免图片下面会有留白*/
}
.column p:nth-of-type(2) {
text-align: center;
background-color: antiquewhite;
}
// 获取列表项外层容器,用来获取该列总高度和添加子项。
var item1 = document.querySelector('.item1');
var item2 = document.querySelector('.item2');
setTimeout(()=>{ // 运用定时器模拟ajax获取图片数据
var arr = [ // 模拟数据
{
text: '这是图片1',
src: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2490556141,282475895&fm=26&gp=0.jpg'
},
{
src: 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2992241689,499733493&fm=26&gp=0.jpg',
text: '这是图片2'
},
{
text: '这是图片3',
src: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2969780621,3804924728&fm=200&gp=0.jpg'
},
{
src: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2006592942,2480446407&fm=200&gp=0.jpg',
text: '这是图片4'
},
{
text: '这是图片5',
src: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2744966649,1683455002&fm=26&gp=0.jpg'
},
{
src: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3189653015,2178768034&fm=26&gp=0.jpg',
text: '这是图片6'
},
{
text: '这是图片7',
src: 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1867339380,1890495727&fm=200&gp=0.jpg'
},
{
src: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=615663329,2871716128&fm=200&gp=0.jpg',
text: '这是图片8'
}
];
arr.forEach((val)=>{ // 通过循环,动态添加数据
new Promise((res,rej)=>{
var img = new Image();
img.src = val.src;
img.onload = function () { // 当图片加载完毕,再执行then后面的代码
res(img);
}
})
.then((img)=>{
if (item1.offsetHeight <= item2.offsetHeight) { // 判断高度最低一列,添加内容
// 往第一列添加列表项
item1.innerHTML +=
`
${val.text}
`;
} else {
// 往第二列添加列表项
item2.innerHTML +=
`
${val.text}
`;
}
})
.catch((error)=>{
console.log(error);
});
});
},100);
var defaultImg = new Image();
defaultImg.src = 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=51715546,2816916450&fm=26&gp=0.jpg';
// 获取列表项外层容器,用来获取该列总高度和添加子项。
var item1 = document.querySelector('.item1');
var item2 = document.querySelector('.item2');
setTimeout(()=>{ // 模拟ajax获取图片数据
var arr = [ // 模拟数据
{
text: '这是图片1',
src: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/i'
},
{
src: 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2992241689,499733493&fm=26&gp=0.jpg',
text: '这是图片2'
},
{
text: '这是图片3',
src: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2969780621,3804924728&fm=200&gp=0.jpg'
},
{
src: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2006592942,2480446407&fm=200&gp=0.jpg',
text: '这是图片4'
},
{
text: '这是图片5',
src: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2744966649,1683455002&fm=26&gp=0.jpg'
},
{
src: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3189653015,2178768034&fm=26&gp=0.jpg',
text: '这是图片6'
},
{
text: '这是图片7',
src: 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1867339380,1890495727&fm=200&gp=0.jpg'
},
{
src: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=615663329,2871716128&fm=200&gp=0.jpg',
text: '这是图片8'
}
];
var promiseAll = [],
img = [];
arr.forEach((val,i)=>{ // 循环加载图片
promiseAll[i] = new Promise((res,rej)=>{
img[i] = new Image();
img[i].src = val.src;
img[i].title = val.text;
img[i].onload = function () { // 图片加载完毕后该promise状态为fulfilled
res(this);
};
img[i].onerror = function () { //图片加载失败,显示默认图片
this.src = defaultImg.src;
this.onload = function () {
res(this);
};
}
});
});
const p = Promise.all(promiseAll).then((img)=>{ //合并promise,这意味着所有promise状态为fulfilled,p的状态才为fulfilled
img.forEach((val)=>{ // 循环添加
if (item1.offsetHeight <= item2.offsetHeight) { // 判断高度最低一列,添加内容
// 往第一列添加列表项
item1.innerHTML +=
`
${val.title}
`;
} else {
// 往第二列添加列表项
item2.innerHTML +=
`
${val.title}
`;
}
});
});
},100);
demo地址:https://caizhichen.github.io/waterfall/javascript.html
vue.js
{ {item.gid}}
div>
div>
div>
{ {item.gid}}
div>
div>
div>
div>
template>
#box {
width: 600px;
border: 10px solid purple;
display: flex;
margin: 40px auto;
}
#box .wrap-box {
height: 100%;
background:sandybrown;
font-size: 32px;
text-align: center;
}
#box img {
width: 300px;
}
export default {
data () {
return {
arr1: [ // 左边一列
],
arr2: [ //右边一列
]
}
},
methods: {
// 通过递归,循环获取高度,插入数据
updataImg (arr) {
if (arr == false) { // 控制递归终止条件
return;
}
var leftHeight = this.$refs.left.offsetHeight;//获取左边一列高度
var rightHeight = this.$refs.right.offsetHeight;
// console.log(leftHeight,rightHeight);
if (leftHeight <= rightHeight) {
this.arr1.push(arr.shift()); // 删除数组的第一项并将删除后的数组添加到arr1中
} else {
this.arr2.push(arr.shift());
}
this.$nextTick(function () {
this.updataImg(arr);
})
},
// 预加载
preloading (arr) {
var defaultImg = new Image();
defaultImg.src = 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=490096197,3796491108&fm=15&gp=0.jpg';
var promiseAll = [],
img = [];
arr.forEach((val,i)=>{
promiseAll[i] = new Promise((res,rej)=>{
img[i] = new Image();
img[i].src = val.src;
img[i].onload = function () {
res();
};
img[i].onerror = function () { // 图片加载失败
this.src = defaultImg.src;
this.onload = function () {
res();
};
}
});
});
const p = Promise.all(promiseAll).then(()=>{ // 加载完毕
this.updataImg(arr);
});
}
},
created: function () {
this.$ajax({
method: 'POST',
url: 'http://localhost/php/goods.php',
})
.then( (response)=>{ //数据格式[{'src':''},{'src':''}]
var data = response.data;
console.log(response.data);
this.preloading(data);
} )
.catch( (error)=>{
console.log(error);
} );
},
mounted: function () {
}
}
你可能感兴趣的:(vue,实例化几种方式)