React 图片瀑布流

news2024/9/21 20:40:34

 思路:

根据浏览器宽度,确定列数,请求的图片列表数据是列数的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' }}>&#8595; 下拉刷新</h3>
            }
            releaseToRefreshContent={
              <h3 style={{ textAlign: 'center' }}>&#8593; 释放刷新</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;}

效果图:

参考链接:

https://chat.xutongbao.top/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1132417.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

22 行为型模式-状态模式

1 状态模式介绍 2 状态模式结构 3 状态模式实现 代码示例 //抽象状态接口 public interface State {//声明抽象方法,不同具体状态类可以有不同实现void handle(Context context); }

PyQt5入门4——给目标检测算法构建一个简单的界面

PyQt5入门4——给目标检测算法构建一个简单的界面 学习前言要构建怎么样的界面实例使用1、窗口构建a、按钮&#xff1a;获取图片b、Inputs、Outputs文本提示c、Inputs、Outputs图片显示d、箭头显示e、整点祝福 2、主程序运行 全部代码 学习前言 搞搞可视化界面哈&#xff0c;虽…

外汇天眼:CySEC向塞浦路斯投资公司的董事会成员发出警告

塞浦路斯证券与交易委员会&#xff08;CySEC&#xff09;已警告塞浦路斯投资公司&#xff08;CIFs&#xff09;的董事会成员&#xff0c;提醒他们加强履行职责&#xff0c;推动诚信和高道德标准的文化&#xff0c;此前监管行动揭示了合规方面的差距。 CySEC已经在加强监管措施…

国产破局、引领三维未来 | 大势智慧2023秋季新品发布会来了!

国产破局、引领三维未来 全国产、真安全 大势智慧2023秋季新品发布会 10.27 | 14:30 扫码预约 敬请期待 #实景三维##三维重建##国产替代##新品发布# ​​​

ts声明文件

1 背景 对于为第三方模块/库写声明文件之前&#xff0c;我们需要知道第三方模块/库&#xff0c;是否需要声明文件&#xff0c;或者是否已有声明文件。 若第三方模块/库&#xff0c;是ts编写且无声明文件&#xff0c; 可以使用--declaration配置选项来生成&#xff1b;可以在命…

什么是SpringCloud Alibaba Nacos注册中心

&#x1f600;前言 本篇博文是关于SpringCloud Alibaba Nacos的基本介绍和使用&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满…

【Java系列】ArrayList

ArrayList 添加元素访问元素修改元素删除元素计算大小迭代数组列表其他的引用类型ArrayList 排序Java ArrayList 方法系列文章系列文章版本记录 引言 ArrayList 类是一个可以动态修改的数组&#xff0c;与普通数组的区别就是它是没有固定大小的限制&#xff0c;我们可以添加或删…

wxPython 布局调试技巧

在Show()与MainLoop()直接加入以上代码 import wx.lib.inspection ...frame.Show() wx.lib.inspection.InspectionTool().Show() app.MainLoop()启动后会弹出布局查看工具

家用洗地机哪种好?2023年最好用的洗地机

洗地机已经成为现代家庭不可或缺的家电之一&#xff0c;可以大大减轻家庭清洁的负担&#xff0c;使我们的生活更加轻松和便捷。但面对市场上众多品牌和型号的洗地机&#xff0c;我们如何选择最适合自己的呢?本文将为您介绍2023年最好用的洗地机&#xff0c;详细比较它们的特点…

Git不常用命令(持续更新)

今日鸡汤&#xff1a;当你最满足的时候&#xff0c;通常也最孤独&#xff1b;当你最愤慨的时候&#xff0c;通常也最可怜。 此博文会列出一些平时不常用&#xff0c;但是能提高效率的git命令&#xff0c;后续会出IDEA对应的操作步骤 快看看你是不是都用过... 分支&#xff08;…

HarmonyOS 多音频播放并发政策及音频管理解析

音频打断策略 多音频并发&#xff0c;即多个音频流同时播放。此场景下&#xff0c;如果系统不加管控&#xff0c;会造成多个音频流混音播放&#xff0c;容易让用户感到嘈杂&#xff0c;造成不好的用户体验。为了解决这个问题&#xff0c;系统预设了音频打断策略&#xff0c;对…

微信群发消息的正确打开方式,让你的社交更高效!

在当今的社交媒体时代&#xff0c;微信已经成为了我们生活中必不可少的一部分。而微信的群发消息功能&#xff0c;让我们可以方便地将信息一次性发送给多个联系人。然而&#xff0c;微信的群发消息功能有一个限制&#xff0c;即每次只能群发200个联系人。这对于需要发送消息给大…

【网安大模型专题10.19】※论文5:ChatGPT+漏洞定位+补丁生成+补丁验证+APR方法+ChatRepair+不同修复场景+修复效果(韦恩图展示)

Keep the Conversation Going: Fixing 162 out of 337 bugs for $0.42 each using ChatGPT 写在最前面背景介绍自动程序修复流程Process of APR (automated program repair)1、漏洞程序2、漏洞定位模块3、补丁生成4、补丁验证 &#xff08;可以学习的PPT设计&#xff09;经典的…

Jmeter(十三):jmeter第三方插件管理工具安装详细步骤

jmeter第三方插件管理工具安装 第一步&#xff1a;下载 jmeter-plugins-manager-1.6.jar 第二步&#xff1a;把该jar包放置到:jmeter安装路径/lib/ext下 第三步&#xff1a;重启jmeter,在选项下可以看见插件管理 同时&#xff0c;我也准备了一份软件测试视频教程&#xff08;…

(10_24)【有奖体验】AIGC小说创作大赛开启!通义千问X函数计算部署AI助手

一个 AI 助手到底能做什么&#xff1f; 可以书写小说 可以解析编写代码 可以鼓舞心灵 提供职业建议 还有更多能力需要您自己去探索。接下来我们将花费 5 分钟&#xff0c;基于函数计算X通义千问部署一个 AI 助手&#xff0c;帮你撰写各类文案。 领取函数计算试用额度 首次开…

折纸问题

折纸的次数 —— 从上到下的折痕 本质上是中序遍历的问题&#xff0c;因为每一次在已有的折痕后折的时候&#xff0c;当前折痕上的折痕一定为凹&#xff0c;当前折痕下的折痕一定为凸 。实际模拟了一个不存在的二叉树结构的中序遍历。 注&#xff1a;折纸折几次整颗二叉树就有…

Jmeter的接口自动化测试

在去年实施了一年的三端&#xff08;PC、无线M站、无线APP【Android、IOS】&#xff09;后&#xff0c;今年7月份开始&#xff0c;我们开始进行接口自动化的实施&#xff0c;目前已完成了整个框架的搭建以及接口的持续测试集成。今天做个简单的分享。 在开始自动化投入前&#…

独家揭秘微信视频号下载提取器,使用方法!

1&#xff1a;微信视频号下载提取器&#xff0c;需要先确认自己手机电脑版本是否支持视频号的观看和浏览 2:需要下载视频号的作品发给视频下载小助手&#xff0c;聊天窗口 3&#xff1a;打开小助手解析视频号视频链接&#xff0c;保存到手机相册或者电脑上 注意视频号电脑版…

还原现场——前端录制用户行为技术方案

一、问题背景 目前&#xff0c;在我们的项目中通常会使用各种各样的埋点和监控来收集页面访问的信息&#xff0c;例如点击埋点、PV埋点等&#xff0c;这些埋点数据能够反应绝大部分的用户行为&#xff0c;但是对于一些关注上下文的使用场景而言这些埋点是不够的。 对于产品而言…

智慧工地云平台源码 人工智能AI+多系统集成+智能预警平台源码

智慧工地云平台源码 人工智能AI多系统集成智能预警平台 智慧工地企业级监管平台融入AIoT、移动互联网和物联网等领先技术&#xff0c;再结合工地“人、机、料、法、环”五大要素&#xff0c;劳务实名制管理、环境监测管理、安全施工管理、质量及能耗管理等智慧化应用&#xff0…