需求
在业务中需要在web页面根据搜索的结果渲染出瀑布流样式的结果页面,我实现效果如下(感觉不太满意,他并不是通过计算最小高度然后自动填补,而是按照几列几行来一个个填补,后面有更深入的研究会更新,这是一种赶工实现的方式):
实现方法
尝试了直接使用flex布局,存在较大的缺陷,虽然看似实现了,但对于特别的图片显示还是很奇怪,不够贴合,后来查询了几种插件,不是说他们都不好,试下的效果应该是类似的,都是通过计算图形的宽高来进行布局的,不过有些对于数据中包含的信息要求比较高,让后台在我的数据里面塞图形宽高已经是我这个小前端的极限了!!最后找到了一个 react-grid-layout 插件,感谢大佬小翼在回答中提供的几种插件。
代码
import React, { Component } from "react";
import styles from "./styles.module.less";
// 引入lodash
import _ from "lodash";
// 引入组件
import RGL, { WidthProvider } from "react-grid-layout";
// 包装组件
const ReactGridLayout = WidthProvider(RGL);
// 定义案例数组数据
const items = [
{
url:
"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=384124318,1246746555&fm=26&gp=0.jpg",
width: 540,
height: 300,
},
{
url:
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2435568812,3959731342&fm=11&gp=0.jpg",
width: 499,
height: 320,
},
{
url:
"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3341500380,460051199&fm=11&gp=0.jpg",
width: 604,
height: 300,
},
{
url:
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2435568812,3959731342&fm=11&gp=0.jpg",
width: 499,
height: 320,
},
{
url:
"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3341500380,460051199&fm=11&gp=0.jpg",
width: 604,
height: 300,
},
{
url:
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3426709235,3578542279&fm=26&gp=0.jpg",
width: 500,
height: 210,
},
{
url:
"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1322120441,3602413475&fm=26&gp=0.jpg",
width: 400,
height: 183,
},
{
url:
"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3426709235,3578542279&fm=26&gp=0.jpg",
width: 500,
height: 210,
},
{
url:
"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3341500380,460051199&fm=11&gp=0.jpg",
width: 604,
height: 300,
},
{
url:
"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1322120441,3602413475&fm=26&gp=0.jpg",
width: 400,
height: 183,
},
{
url:
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi2.hdslb.com%2Fbfs%2Farchive%2F3c7a5b24b38ac3216182e0bf026622801d10c3fa.jpg&refer=http%3A%2F%2Fi2.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1612319581&t=9486804e3256336fd0d2c2865d929d6c",
width: 1728,
height: 1080,
},
{
url:
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.51yuansu.com%2Fpic3%2Fcover%2F02%2F85%2F29%2F5a61c19d6a659_610.jpg&refer=http%3A%2F%2Fpic.51yuansu.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1612319668&t=aaa48c2a20b18f021f8058d7bd882dfb",
width: 610,
height: 1295,
},
];
// 定义一个父组件,主要是为了进行容器大小的变化监听,然后动态设置高度
export default class About extends Component {
constructor(props) {
super(props);
// 行高初始化,这是我自己的行高,根据当前容器大小计算出来的
this.state = { rowHeight: 110 };
}
componentDidMount() {
// 初始化size监听函数
this.screenChange();
}
// 卸载size监听函数
componentWillUnmount() {
window.removeEventListener("resize", this.resize);
}
// size监听函数
screenChange = () => {
window.addEventListener("resize", this.resize);
};
// 区域宽高变化时触发行高的变化
resize = () => {
const box = document.getElementById("watar-fall");
let width = box.clientWidth;
let rowHeight = (width - 100) / 12;
this.setState({ rowHeight });
console.log(width);
};
render() {
// 将行高作为props传入子组件
const { rowHeight } = this.state;
return (
);
}
}
// 定义的瀑布流子组件,真是应用可以单独封装在外部
class WaterFall extends Component {
// 默认的props
static defaultProps = {
className: "layout",
isDraggable: false,
isResizable: false,
cols: 12,
rowHeight: 110,
};
constructor(props) {
super(props);
// 初始化定义layout 和渲染的数据
this.state = { layout: this.generateLayout([]), data: [] };
}
// 加载的时候更新layout和data
componentDidMount() {
this.setState({
data: [...this.props.items],
layout: this.generateLayout(this.props.items),
});
}
// 生成每一个瀑布流内item 的函数
generateDOM = () => {
const { data } = this.state;
return _.map(_.range(data.length), function (i) {
return (
// 这里的key值和layout里面的要对应,所以不要用其他值代替
{/*在这里可以自定义你要的显示样式 */}
);
});
};
// 生成layout的数组函数
generateLayout = (items) => {
// 循环数据返回相应的layout
return _.map(new Array(items.length), function (item, i) {
// 在这里设置每一个图片的高度。我是通过图片的宽然后根据所占行的比例,计算出来的等比高度
let y = (3 / items[i].width) * items[i].height;
return {
x: (i * 3) % 12, // 每个图片的起始x点 ,因为是4列,所以在12列里面是3列一个图片
y: Math.floor(i / 4) * y, // 间隔四个换行
w: 3, // 图片占的列数
h: y, // 计算所得的图片高度
i: i.toString(), // layout的key值
};
});
};
render() {
return (
{/* 传入参数并生成瀑布流 */}
{this.generateDOM()}
);
}
}
总结
这次基本的实现是这样,这个组件本来更强大的地方应该是在可拖拽的方面,我这算是因小失大了,后面如果有时间再研究一下其他瀑布流或者自己写一个js组件吧。(如果我写的有什么问题,欢迎指正!)