React 图片瀑布流
思路:
根据浏览器宽度,确定列数,请求的图片列表数据是列数的10倍,按列数取数据渲染
Index.js:
import React from 'react'
import { connect } from 'react-redux'
import { withRouter } from 'react-router-dom'
import { SinglePageHeader } from '../../../../../components/light'
import InfiniteScroll from 'react-infinite-scroll-component'
import { Divider, Skeleton } from 'antd'
import useList from './useList'
import LazyLoad from 'react-lazy-load'
import './index.css'
function Index(props) {
const {
dataSource,
isHasMore,
columnCount,
handleSearch,
handleImgDrawSameStyleClick,
} = useList(props)
return (
<div className="m-ai-img-wrap-box">
<div className={`m-ai-img-wrap-chat`}>
<SinglePageHeader title="AI绘画作品展示"></SinglePageHeader>
<div className="m-ai-img-list" id="scrollableDiv">
<InfiniteScroll
dataLength={dataSource.length}
next={handleSearch}
refreshFunction={() => handleSearch({ page: 1, isRefresh: true })}
pullDownToRefresh
pullDownToRefreshThreshold={50}
pullDownToRefreshContent={
<h3 style={{ textAlign: 'center' }}>↓ 下拉刷新</h3>
}
releaseToRefreshContent={
<h3 style={{ textAlign: 'center' }}>↑ 释放刷新</h3>
}
hasMore={isHasMore}
loader={
<Skeleton
avatar
paragraph={{
rows: 3,
}}
active
className="m-h5-lesson-play-skeleton"
/>
}
endMessage={
dataSource.length === 0 ? null : (
<Divider plain>已经到底啦~</Divider>
)
}
scrollableTarget="scrollableDiv"
>
<div className="m-ai-img-list-inner">
{Array.from({ length: columnCount }, () => '').map(
(item, index) => (
<div className="m-ai-img-list-column" key={index}>
{dataSource
.filter(
(item, dataSourceIndex) =>
dataSourceIndex % columnCount === index
)
.map((item) => (
<div key={item.imgUid}>
<LazyLoad className="m-ai-img-lazy-load">
<img
src={item.imgUrlCdn}
className="m-ai-img"
alt="图片"
onClick={() => handleImgDrawSameStyleClick(item)}
></img>
</LazyLoad>
</div>
))}
</div>
)
)}
</div>
{dataSource.length === 0 ? (
<Skeleton
avatar
paragraph={{
rows: 3,
}}
active
className="m-h5-lesson-play-skeleton"
/>
) : null}
</InfiniteScroll>
</div>
</div>
</div>
)
}
const mapStateToProps = (state) => {
return {
collapsed: state.getIn(['light', 'collapsed']),
isRNGotToken: state.getIn(['light', 'isRNGotToken']),
}
}
const mapDispatchToProps = (dispatch) => {
return {
onSetState(key, value) {
dispatch({ type: 'SET_LIGHT_STATE', key, value })
},
onDispatch(action) {
dispatch(action)
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Index))
useList.js:
import { useState, useEffect } from 'react'
import { Form } from 'antd'
import Api from '../../../../../api'
import { message } from 'antd'
import * as clipboard from 'clipboard-polyfill/text'
export default function useList(props) {
const [total, setTotal] = useState(10)
const [current, setCurrent] = useState(1)
let tempCount = Math.floor((window.innerWidth - 10) / 180)
tempCount = Math.floor((window.innerWidth - (5 + tempCount * 5)) / 180)
console.log('tempCount1', tempCount)
//把dataSource和pageSize单独放在一起是为了避免切换pageSize时的bug
const [state, setState] = useState({
dataSource: [],
pageSize: tempCount * 10,
})
const [isHasMore, setIsHasMore] = useState(true)
// eslint-disable-next-line
const [username, setUsername] = useState(localStorage.getItem('username'))
const [form] = Form.useForm()
// eslint-disable-next-line
const [initValues, setInitValues] = useState({})
// eslint-disable-next-line
const [columnCount, setColumnCount] = useState(tempCount)
//搜索
const handleSearch = ({
page = current,
pageSize = state.pageSize,
isRefresh = false,
} = {}) => {
if (isRefresh) {
setState({
dataSource: [],
pageSize: tempCount * 10,
})
}
let searchData = { pageNum: page, pageSize }
Api.h5.sdImgSearch(searchData).then((res) => {
if (res.code === 200) {
const { pageNum, pageSize, total } = res.data
let list = res.data.list
if (isRefresh) {
setState({
dataSource: [...list],
pageSize: res.data.pageSize,
})
} else {
setState({
dataSource: [...state.dataSource, ...list],
pageSize: res.data.pageSize,
})
}
setTotal(res.data.total)
const currentTemp = res.data.pageNum + 1
setCurrent(currentTemp)
setIsHasMore(pageNum < Math.ceil(total / pageSize))
}
})
}
//添加或编辑
const handleFinish = (values) => {
console.log('Success:', values)
Api.h5.exchangeCodeAppUse(values).then((res) => {
if (res.code === 200) {
message.success('恭喜您,兑换成功')
//props.history.push('/h5/index/me')
}
})
}
//校验失败
const handleFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo)
}
//退出
const handleQuit = () => {
Api.light.userLogout().then((res) => {
if (res.code === 200) {
props.history.push('/h5/login')
window.localStorage.removeItem('username')
window.localStorage.removeItem('token')
}
})
}
//跳转
const handleJumpPage = (path) => {
// eslint-disable-next-line
props.history.push(path)
}
const handleCopy = (text) => {
clipboard.writeText(text).then(() => {
message.success('复制成功')
})
}
const handleImgDrawSameStyleClick = (item) => {
console.log(item)
props.history.push(
`/single/home/sdSimple?modelId=${item.id}&name=${item.name}&link=${item.link}&imgUid=${item.imgUid}`
)
}
useEffect(() => {
if (window.platform === 'rn') {
if (props.isRNGotToken === true) {
handleSearch()
}
} else {
handleSearch()
}
// eslint-disable-next-line
}, [props.isRNGotToken])
return {
username,
form,
initValues,
dataSource: state.dataSource,
total,
current,
pageSize: state.pageSize,
isHasMore,
columnCount,
handleFinish,
handleFinishFailed,
handleQuit,
handleJumpPage,
handleCopy,
handleSearch,
handleImgDrawSameStyleClick,
}
}
index.css:
.m-ai-img-wrap-box{display: flex;justify-content: center;background: #ddd;background: #ddd;position: absolute;top: 0;left: 0;right: 0;bottom: 0;overflow: hidden;}
.m-ai-img-wrap-chat{position: relative; display: flex;flex-direction: column;width: 100%;background: #ededed;}
.m-ai-img-main{flex:1;display: flex;flex-direction: column;overflow-y: auto;}
.m-ai-img-list{flex: 1;padding: 0px 0;overflow-y: auto;}
.m-ai-img-list-inner{position: relative;padding: 0 0 0 5px; display: flex; flex-wrap: wrap;justify-content: center;}
.m-ai-img-list-column{display: flex;flex-direction: column;width: 175px;margin: 0 5px 0 0;}
.m-ai-img-lazy-load{position: relative;min-width: 175px; display: flex;flex-direction: column;justify-content: center; min-height: 175px;margin: 0 0 5px 0;border-radius: 5px; background: #dddddd;}
.m-ai-img{width: 175px;border-radius: 5px;}
效果图:
参考链接: