使用react-virtualized实现图片动态高度长列表的问题


Posted in Javascript onMay 28, 2021

虚拟列表是一种根据滚动容器元素的可视区域来渲染长列表数据中某一个部分数据的技术。虚拟列表是对长列表场景一种常见的优化,毕竟很少有人在列表中渲染上百个子元素,只需要在滚动条横向或纵向滚动时将可视区域内的元素渲染出即可。

开发中遇到的问题

1.长列表中的图片要保持原图片相同的比例,那纵向滚动在宽度不变的情况下,每张图片的高度就是动态的,当该列表项高度发生了变化,会影响该列表项及其之后所有列表项的位置信息。

2.图片width,height必须在图片加载完成后才能获得.

解决方案

我们使用react-virtualized中list组件,官方给出的例子

import React from 'react';
import ReactDOM from 'react-dom';
import {List} from 'react-virtualized';

// List data as an array of strings
const list = [
  'Brian Vaughn',
  // And so on...
];

function rowRenderer({
  key, // Unique key within array of rows
  index, // Index of row within collection
  isScrolling, // The List is currently being scrolled
  isVisible, // This row is visible within the List (eg it is not an overscanned row)
  style, // Style object to be applied to row (to position it)
}) {
  return (
    <div key={key} style={style}>
      {list[index]}
    </div>
  );
}

// Render your list
ReactDOM.render(
  <List
    width={300}
    height={300}
    rowCount={list.length}
    rowHeight={20}
    rowRenderer={rowRenderer}
  />,
  document.getElementById('example'),
);

使用react-virtualized实现图片动态高度长列表的问题

其中rowHeight是每一行的高度,可以传入固定高度也可以传入function。每次子元素高度改变需要调用recomputeRowHeights方法,指定索引后重新计算行高度和偏移量。

具体实现

const ImgHeightComponent = ({ imgUrl, onHeightReady, height, width }) => {
  const [style, setStyle] = useState({
    height,
    width,
    display: 'block',
  })
  const getImgWithAndHeight = (url) => {
    return new Promise((resolve, reject) => {
      var img = new Image()
      // 改变图片的src
      img.src = url
      let set = null
      const onload = () => {
        if (img.width || img.height) {
          //图片加载完成
          clearInterval(set)
          resolve({ width: img.width, height: img.height })
        }
      }
      set = setInterval(onload, 40)
    })
  }

  useEffect(() => {
    getImgWithAndHeight(imgUrl).then((size) => {
      const currentHeight = size.height * (width / size.width)
      setStyle({
        height: currentHeight,
        width: width,
        display: 'block',
      })
      onHeightReady(currentHeight)
    })
  }, [])
  return <img src={imgUrl} alt=''  style={style} />
}

先写一个获取图片高度的组件,通过定时循环检测获取并计算出高度传给父组件。

import React, { useState, useEffect, useRef } from 'react'
import styles from './index.scss'
import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer'
import { List  } from 'react-virtualized/dist/commonjs/List'

export default class DocumentStudy extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      list: [], 
      heights: [],
      autoWidth:900,
      autoHeight: 300
    }
  }

  handleHeightReady = (height, index) => {
    this.setState(
      (state) => {
        const flag = state.heights.some((item) => item.index === index)
        if (!flag) {
          return {
            heights: [
              ...state.heights,
              {
                index,
                height,
              },
            ],
          }
        }
        return {
          heights: state.heights,
        }
      },
      () => {
        this.listRef.recomputeRowHeights(index)
      },
    )
  }

  getRowHeight = ({ index }) => {
    const row = this.state.heights.find((item) => item.index === index)
    return row ? row.height : this.state.autoHeight
  }

  renderItem = ({ index, key, style }) => {
    const { list, autoWidth, autoHeight } = this.state
    if (this.state.heights.find((item) => item.index === index)) {
      return (
        <div key={key} style={style}>
          <img src={list[index].imgUrl}  alt='' style={{width: '100%'}}/>
        </div>
      )
    }

    return (
      <div key={key} style={style}>
        <ImgHeightComponent
          imgUrl={list[index].imgUrl}
          width={autoWidth}
          height={autoHeight}
          onHeightReady={(height) => {
            this.handleHeightReady(height, index)
          }}
        />
      </div>
    )
  }

  render() {
    const { list } = this.state
    return (
      <>
        <div style={{ height: 1000 }}>
          <AutoSizer>
            {({ width, height }) => (
              <List
                ref={(ref) => (this.listRef = ref)}
                width={width}
                height={height}
                overscanRowCount={10}
                rowCount={list.length}
                rowRenderer={this.renderItem}
                rowHeight={this.getRowHeight}
              />
            )}
          </AutoSizer>
        </div>
      </>
    )
  }
}

父组件通过handleHeightReady方法收集所有图片的高度,并在每一次高度改变调用List组件的recomputeRowHeights方法通知组件重新计算高度和偏移。到这里基本已经解决遇到的问题。

实际效果

使用react-virtualized实现图片动态高度长列表的问题

小结

目前只是使用react-virtualized来完成图片长列表实现,具体react-virtualized内部实现还需要进一步研究。

以上就是用react-virtualized实现图片动态高度长列表的详细内容,更多关于react virtualized长列表的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
JS无限树状列表实现代码
Jan 11 Javascript
jQuery Tools tab使用介绍
Jul 14 Javascript
JavaScript实现鼠标点击后层展开效果的方法
May 13 Javascript
JavaScript的9种继承实现方式归纳
May 18 Javascript
浅谈JavaScript变量的自动转换和语句
Jun 12 Javascript
jQuery实现两列等高并自适应高度
Dec 22 Javascript
微信小程序 欢迎页面的制作(源码下载)
Jan 09 Javascript
JavaScript使用delete删除数组元素用法示例【数组长度不变】
Jan 17 Javascript
Vue中的数据监听和数据交互案例解析
Jul 12 Javascript
小程序实现图片预览裁剪插件
Nov 22 Javascript
OpenLayers3实现对地图的基本操作
Sep 28 Javascript
JS前端可视化canvas动画原理及其推导实现
Aug 05 Javascript
element多个表单校验的实现
May 27 #Javascript
springboot+VUE实现登录注册
May 27 #Vue.js
vue+springboot实现登录验证码
vue+spring boot实现校验码功能
May 27 #Vue.js
vue-cropper组件实现图片切割上传
May 27 #Vue.js
vue-cropper插件实现图片截取上传组件封装
May 27 #Vue.js
HTML+VUE分页实现炫酷物联网大屏功能
You might like
php5中类的学习
2008/03/28 PHP
基于Discuz security.inc.php代码的深入分析
2013/06/03 PHP
php中的比较运算符详解
2013/10/28 PHP
PHP小教程之实现链表
2014/06/09 PHP
PHP运行模式汇总
2016/11/06 PHP
[JS]点出统计器
2020/10/11 Javascript
jQuery.clean使用方法及思路分析
2013/01/07 Javascript
JavaScript定义数组的三种方法(new Array(),new Array('x','y')
2016/10/04 Javascript
使用Fullpage插件快速开发整屏翻页的页面
2017/09/13 Javascript
Bootstrap 模态框多次显示后台提交多次BUG的解决方法
2017/12/26 Javascript
360doc网站不登录就无法复制内容的解决方法
2018/01/27 Javascript
JavaScript实现的文本框placeholder提示文字功能示例
2018/07/25 Javascript
JS中自定义事件的使用与触发操作实例分析
2019/11/01 Javascript
原生js实现自定义难度的扫雷游戏
2021/01/22 Javascript
零基础写python爬虫之抓取百度贴吧并存储到本地txt文件改进版
2014/11/06 Python
python获取标准北京时间的方法
2015/03/24 Python
Python3中简单的文件操作及两个简单小实例分享
2017/06/18 Python
opencv与numpy的图像基本操作
2019/03/08 Python
通过cmd进入python的实例操作
2019/06/26 Python
python 并发编程 多路复用IO模型详解
2019/08/20 Python
Python爬虫之Spider类用法简单介绍
2020/08/04 Python
python3实现简单飞机大战
2020/11/29 Python
如何用border-image实现文字气泡边框的示例代码
2020/01/21 HTML / CSS
美国时装品牌:Nautica(诺帝卡)
2016/08/28 全球购物
世界上最大的巴士旅游观光公司:Big Bus Tours
2016/10/20 全球购物
什么是ESB?请介绍一下ESB?
2015/05/27 面试题
房地产管理毕业生自荐信
2013/11/04 职场文书
综合实践教学反思
2014/01/31 职场文书
12月红领巾广播稿
2014/02/13 职场文书
师德师风演讲稿
2014/05/05 职场文书
总经理任命书范本
2014/06/05 职场文书
2015年事业单位办公室文员工作总结
2015/04/24 职场文书
初三毕业感言
2015/07/31 职场文书
Golang中异常处理机制详解
2021/06/08 Golang
spring boot中nativeQuery的用法
2021/07/26 Java/Android
vue配置型表格基于el-table拓展之table-plus组件
2022/04/12 Vue.js