vue 实例化几种方式_现代前端进阶教程之(二):瀑布流布局纯css和javascript、vue实现瀑布流布局的几种方式... - Go语言中文社区

vue 实例化几种方式_现代前端进阶教程之(二):瀑布流布局纯css和javascript、vue实现瀑布流布局的几种方式...


因为专题制作需要用到瀑布流,所以这阵子总结了几种实现瀑布流的方式。

纯css实现瀑布流主要有3种方式:

    1. 多列布局multi-columns

    2.Flexbox布局

    3. grid布局

multi-columns


这是columns的语法:http://www.webhek.com/post/css3-multi-columns.html

html代码

<div id="box">

    <div class="item">

    div>

    <div class="item">

    div>

div>

 html结构非常简单,id:box元素是瀑布流的容器,我们的目的是让它的子元素呈现瀑布流排列。这里面可以放置n个项。

css代码

#box {

width: 400px;

column-width: 50%; /*设定列宽*/

    column-count: 2; /*列数*/

    column-gap: 0; /*列间距*/

}

.item {

    break-inside: avoid; /*避免在元素内部断行并产生新列*/

}


你只要在父级元素中设定列数column-count,列间距column-gap,列宽column-width,子元素就会呈现瀑布流排列。

然后在子元素中设定break-inside,这是为了避免子元素内部的文本块分解成单独的列。

我比较喜欢子容器刚好占满父容器,然后通过padding和margin来设置间距。

接下来就可以在item中填充图片和文字。

f268829bfbbfff2213159fbf3f049022.png

multi-columns实现瀑布流非常简单,能兼容IE10及以上。

但是只能够一列一列的排列。

demo:https://caizhichen.github.io/waterfall/column.html

flexbox


和multi-columns相比,flexbox可以说是广为人知了。

html代码

<div id="box">

<div class="column">

<div>item1div>

<div>item2div>

<div>item3div>

div>

<div class="column">

<div>item1div>

<div>item2div>

<div>item3div>

div>

<div class="column">

<div>item1div>

<div>item2div>

<div>item3div>

div>

<div class="column">

<div>item1div>

<div>item2div>

<div>item3div>

div>

div>

flexbox瀑布流需要2层包裹item。

css代码

#box {

width: 600px;

display: flex;

background: red;

}

.column {

width: 25%;

display: flex;

flex-direction: column;

}

.column div {

width: 100%;

}

通过设置2层容器,分别设定为行flex-direction:row(默认)和列flex-direction:column。

横向flex布局嵌套多列纵向flex布局,

然后在往子容器中添加内容模块。

这种方式比起multi-columns来说,更麻烦,并且无法自动堆叠。

grid


grid布局语法:https://www.html.cn/archives/8510

grid正在得到众多浏览器的支持,并且比flexbox更强大,我觉得很有学习的必要。

相比前面2个只能通过列排列的瀑布流来说,grid布局则可以实现横向排列的瀑布流。

html代码

<div id="box">

<div class="item">div>

<div class="item">div>

<div class="item">div>

<div class="item">div>

div>

css代码

#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-rowgrid-column来指定每个子元素所在的区域。

148744dfc2e55dd0e43dbb0c02dc1bab.png

demo:https://caizhichen.github.io/waterfall/grid.html

通过总结css3的三种瀑布流实现方式,我发现,他们虽然能够实现瀑布流布局,但却不符合我的项目需求。

需求:一次加载10张,滑动到图片底部,再加载10张。说白了就是移动端横向瀑布流配合无限滚动。

这是最常见的瀑布流使用方式了。

可惜是multi-columns通过列排列的,它会造成图片的乱序(因为我是下滑加载)。

所以,配合javascript的瀑布流还是不可或缺。

javascript


原理:

    1.将一个容器分为n列。

    2.判断这些列高度最低的一列,往这个列添加列表项

    3.通过循环,重复添加,直到数据添加完毕。

html代码

<div id="waterfall">

<div class="column">

<div class="item1">

div>

div>

<div class="column">

<div class="item2">

div>

div>

div>

html主要分成3层,id:waterfall为瀑布流总容器,class:column是它的子元素,将总容器划分为2列。

接下来可以直接在class:column中放置列表项了。

不过因为要计算每一列的内容高度(我采用flexbox,所以class:column元素高度充满父容器),

我在class:column里面又添加了一层容器,用来放置列表项。这是为了计算列表项的总高度。

css代码

#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;

}

css代码非常简单,设置容器的宽度,高度通过内容来撑开。

接下来是js,任务是动态的往高度最低的列添加列表项。

js代码

// 获取列表项外层容器,用来获取该列总高度和添加子项。

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);

js代码的目的:

    1.判断高度最低的列

    2.往这一列添加列表项

但在实际过程中,因为是通过图片高度来撑开父级,也就是class:item1和class:item2。

而图片是异步加载的,所以要获取最低高度,我们必须在图片加载完毕后再进行高度获取。

所以我采用的方法是结合promise(promise可以看阮一峰的《es6入门标准》)和

图片onload事件(onload 事件在图片加载完成后立即执行,是异步回调)。

1.创建img图片对象,并添加src属性。

2.img.onload事件函数内执行res(),这可以保证在图片加载完毕后才执行then回调函数中的内容。

3.在then回调函数中,判断内容高度,往高度最低的一列中添加列表项。

4.通过循环载入全部数据。

3fe8c2bac746fde3f7c09222af7fc1d2.png

这样就可以实现js瀑布流了。

但是实际上这还存在2个小问题。

1.图片加载并不是一张加载完毕后再加载另一张的。

如果前面的图片很大,也许后面的图片会先加载完毕。

这就会造成图片的乱序,(只会造成同一批加载的图片位置乱序)。

虽然造成的影响不是很大,但还是不够完美。

这可以采用图片预加载来完美解决。

第二个问题是不够完善,如果图片加载失败显示默认图片。

js代码

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);

图片预加载有多种方式,我是通过Promise.all来做的。

Promise.all可以将多个promise实例包装成一个新的promise,并且当所有promise状态完成fulfilled,这个新的promise状态也为完成。

promise的状态为fulfilled才会执行then回调函数。

所以我们只要在那些promise中加载图片,当图片加载完毕后再修改状态fulfilled。

然后在新的promise中处理数据,就能实现图片预加载。

同时,我们可以监听onerror事件,当图片加载失败,显示为默认图片,再修改promise状态。

这样就可以实现一个还算ok的js瀑布流了。

还有一种方案,图片的懒加载,需要后台发送图片的宽高。

下面是效果,人的那张图片是加载失败的默认图片。

76ba367565e093b3b868a6c911f1f5a8.png

demo地址:https://caizhichen.github.io/waterfall/javascript.html

vue.js


接下来是vue怎么实现瀑布流。

因为我的专题是用vue做的,其实和js原理都是一样的,就是判断最低高度,添加列表项。

但在实际做的时候,有一个需要注意的地方。

html代码

<template>

<div id="box">

<div class="wrap-box">

<div class="item1" ref="left">

<div v-for="item of arr1">

<img :src="item.src" alt="">

{{item.gid}}

div>

div>

div>

<div class="wrap-box">

<div class="item2" ref="right">

<div v-for="item of arr2">

<img :src="item.src" alt="">

{{item.gid}}

div>

div>

div>

div>

template>

html代码和js的一样,只是绑定上数据。

css代码

#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;

}

js代码

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和js的原理都是一致的,我们只需要控制2个数组的值。

唯一需要注意的是,vue.$nextTick函数,在官方文档中的解释:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

因为我们需要获取列表项的高度,调用这个函数可以让我们获取到DOM更新后的高度。

但是不能直接循环获取,因为我们需要每添加一个列表项,就马上获取高度进行比较,然后再添加另一个列表项。

如果直接在vue.$nextTick函数中循环数据,那得到的高度都是一致的。

所以我用了递归的方式,每添加一个列表项,就调用vue.$nextTick函数来获取高度和添加列表项。

674b2c0395df0e6020bf036e1fcaa428.png

感觉瀑布流布局还是离不开js,因为往往要添加无限滚动功能,这些是运用css的瀑布流无法解决的。

04afe63b52e837955ceeeb31f904f33a.png

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_42676876/article/details/112332838
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-06-12 19:07:12
  • 阅读 ( 1142 )
  • 分类:前端

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢