前言
要实现如下瀑布流效果,动态图片,动态高度
我知道使用JS能够实现完美瀑布流,但小程序不比web,坑点会比较多,因此我决定先使用CSS看能不能解决,最后实在不行在使用JS来实现
根据网上的教程尝试使用css的方式(column和flex)实现效果,但排列顺序都是竖排而不是横排,不符合产品需求,实现效果如下
GRID瀑布流
如此看来只剩grid这一条路了,还好成功了
基础版
下列摘自网上使用GRID实现瀑布流的实例
模板
<view class="waterfall">
<view class="item">1view>
<view class=“item”>2view> <view class=“item”>3view> <view class=“item”>4view> <view class=“item”>5view> <view class=“item”>6view> … 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动态输出
模板
<page>
<view class="waterfall" style="{{waterStyle}}">
<view class="item item-1">...view>
view> <view class=“waterfall” style="{{waterStyle}}" wx:for="{{items}}" wx:key=…> <view class=“item” style=“grid-row: var(–item-span-{{index}})”>…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
得出,反而效果更好,具体原因我也一脸懵逼,如果有知道的留言告诉我