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>

   

    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中填充图片和文字。

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

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

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

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

flexbox


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

html代码

item1div>

item2div>

item3div>

div>

item1div>

item2div>

item3div>

div>

item1div>

item2div>

item3div>

div>

item1div>

item2div>

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>

div>

div>

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

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

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

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

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

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

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

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

javascript


原理:

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

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

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

html代码

div>

div>

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.通过循环载入全部数据。

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

这样就可以实现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瀑布流了。

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

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

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

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

vue.js


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

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

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

html代码