小程序使用Grid和css变量实现瀑布流布局
前言
要实现如下瀑布流效果,动态图片,动态高度
[图片]
我知道使用JS能够实现完美瀑布流,但小程序不比web,坑点会比较多,因此我决定先使用CSS看能不能解决,最后实在不行在使用JS来实现
根据网上的教程尝试使用css的方式(column和flex)实现效果,但排列顺序都是竖排而不是横排,不符合产品需求,实现效果如下
[图片]
GRID瀑布流
如此看来只剩grid这一条路了,还好成功了
基础版
下列摘自网上使用GRID实现瀑布流的实例
模板
[代码]
1/view>
2/view>
3/view>
4/view>
5/view>
6/view>
...
/view>
[代码]
wxss
[代码].waterfall {
display: grid;
grid-template-columns: repeat(2, 1fr); // 指定两列,自动宽度
grid-gap: 0.25em; // 横向,纵向间隔
grid-auto-flow: row dense; // 是否自动补齐空白
grid-auto-rows: 20px; // base高度,grid-row基于此运算
}
.waterfall .item {
width: 100%;
background: #222;
color: #ddd;
}
.waterfall .item:nth-of-type(3n+1) {
grid-row: auto / span 5;
}
.waterfall .item:nth-of-type(3n+2) {
grid-row: auto / span 6;
}
.waterfall .item:nth-of-type(3n+3) {
grid-row: auto / span 8;
}
[代码]
效果
[图片]
基础版的问题
看看上面的css是如何使用grid实现
[代码].waterfall .item:nth-of-type(3n+1) {
grid-row: auto / span 5;
}
[代码]
上述代码指定[代码]1,4,7,10...[代码]等item的高度,[代码]auto[代码]为grid自动设置该item的起始位置,[代码]span 5[代码]则指定该item的高度为[代码]grid-auto-rows * 5[代码], [代码]grid-auto-rows[代码]在CSS的设定中为20px,在源码中我做了说明,它是一个基础高度
[代码].waterfall .item:nth-of-type(3n+2) {
grid-row: auto / span 6;
}
[代码]
上述代码指定[代码]2,8,11,14...[代码]等item的高度,[代码]auto[代码]为grid自动设置该item的起始位置,[代码]span 6[代码]则指定该item的高度为[代码]grid-auto-rows * 6[代码]
[代码].waterfall .item:nth-of-type(3n+3) {
grid-row: auto / span 8;
}
[代码]
上述代码指定[代码]3,6,12,15...[代码]等item的高度,[代码]auto[代码]为grid自动设置该item的起始位置,[代码]span 8[代码]则指定该item的高度为[代码]grid-auto-rows * 8[代码]
基础版虽然看上去基本符合我们的产品需求,但由css可以知道,它的问题是__高度固定__,但业务上我们并不确切知道每个item的高度及所包含的图片的高度。所以接下来我们要解决__动态高度__设定的问题,让每一个item都自动计算自己的高度,而不是通过CSS来手动指定
css变量登场
微信小程序swiper的自适应高度
小程序中使用css var变量,使js可以动态设置css样式属性
上面两篇文章是之前写得关于css变量的一些巧妙的用法,css变量确实能够解决很多之前很棘手的问题,此时我脑海里面迸发出了一个绝佳的IDEA
仔细观察这段css
[代码].waterfall .item:nth-of-type(3n+2) {
grid-row: auto / span 6;
}
[代码]
唯一不确定的就是[代码]6[代码],对,它应该是一个变量而不是一个恒量,它应该是一个与高度关联的比值,而我们可以通过css变量动态设置这个比值,它大概应该长这样
[代码]page{
--item-span: x // 需要使用setData设置x值
}
.waterfall .item {
grid-row: var(--item-span);
}
[代码]
考虑到需要设置每个item的高度,应该为每一个item设定独立的样式
[代码].waterfall{
--item-span-1: x; // 使用setData设置x值
--item-span-2: y // 使用setData设置y值
}
.waterfall .item-1 {
grid-row: var(--item-span-1);
}
.waterfall .item-2 {
grid-row: var(--item-span-2);
}
[代码]
原理
到此我们就可以讲通如何实现的原理了
注意:下面的例子使用内联样式代替上面的样式设定,因为内联样式可以由JS动态输出
模板
[代码]
.../view>
/view>
.../view>
/view>
/page>
[代码]
Page
[代码]Page({
data: {
waterStyle: '',
items: [...]
},
onReady(){
let query = wx.createSelectorQuery().in(this)
query.selectAll('.waterfall .item').boundingClientRect(ret=>{
let styleStr = '';
ret.forEach((ele, ii) => {
let height = ele.height
let span = parseInt(height/ 20) // 20 = grid-auto-row
styleStr += `--item-span-${ii}: auto / span ${span};`
})
this.setData({
waterStyle: styleStr
})
})
}
})
// 注释一
// 所有item的css变量合集 waterStyle
/*
xn在onReady方法中计算得出
--item-span-1: auto/span x1;
--item-span-2: auto/span x2;
--item-span-3: auto/span x3;
...
*/
[代码]
使用内联样式而不是.item-n
[代码][代码]item元素使用内联样式,因为我们不确定item的数量。
高度计算
通过query我们可以获取所有item子元素的rect属性(长宽高…),计算height属性与grid-auto-row的比值,即我们需要的设定值
waterStyle
参考上述代码的注释一(动态计算每一个item的span比值),通过setData方法设置生效(设置在父级[代码]view.waterfall[代码]上),如此grid会自动设置每一个item子元素的位置
优化
以上基本将如何使用grid实现瀑布流的原理阐述了一遍,实际代码中需要注意[代码]grid-auto-row[代码]值的设定,在我们的项目中省略了此项css属性的设置,即span比值实际是由[代码]height/grid-gap[代码]得出,反而效果更好,具体原因我也一脸懵逼,如果有知道的留言告诉我
[图片]