使用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运行耗时操作的延时显示方法
Nov 19 Javascript
JQuery操作tr和td内容的方法实例
Mar 06 Javascript
仿当当网淘宝网等主流电子商务网站商品分类导航菜单
Sep 25 Javascript
jQuery如何实现点击页面获得当前点击元素的id或其他信息
Jan 09 Javascript
js如何调用qq互联api实现第三方登录
Mar 28 Javascript
jQuery循环动画与获取组件尺寸的方法
Feb 02 Javascript
本人自用的global.js库源码分享
Feb 28 Javascript
jquery验证邮箱格式并显示提交按钮
Nov 07 Javascript
基于jquery实现页面滚动到底自动加载数据的功能
Dec 19 Javascript
AjaxUpLoad.js实现文件上传功能
Mar 02 Javascript
Angular项目如何升级至Angular6步骤全纪录
Sep 03 Javascript
leaflet加载geojson叠加显示功能代码
Feb 21 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
php在多维数组中根据键名快速查询其父键以及父键值的代码
2011/05/07 PHP
php继承中方法重载(覆盖)的应用场合
2015/02/09 PHP
php生成二维码
2015/08/10 PHP
PHP赋值的内部是如何跑的详解
2019/01/13 PHP
JavaScript接口实现代码 (Interfaces In JavaScript)
2010/06/11 Javascript
设置checkbox为只读(readOnly)的两种方式
2013/10/11 Javascript
JS弹出层单纯的绝对定位居中示例代码
2014/02/18 Javascript
js图片处理示例代码
2014/05/12 Javascript
jquery网页回到顶部效果(图标渐隐,自写)
2014/06/16 Javascript
Nodejs中自定义事件实例
2014/06/20 NodeJs
利用jQuery实现漂亮的圆形进度条倒计时插件
2015/09/30 Javascript
谈谈JSON对象和字符串之间的相互转换JSON.stringify(obj)和JSON.parse(string)
2015/10/01 Javascript
jQuery EasyUI中DataGird动态生成列的方法
2016/04/05 Javascript
AngularJS基础 ng-non-bindable 指令详细介绍
2016/08/02 Javascript
JS实用的带停顿的逐行文本循环滚动效果实例
2016/11/23 Javascript
Angualrjs和bootstrap相结合实现数据表格table
2017/03/30 Javascript
详解Angular 4.x NgIf 的用法
2017/05/22 Javascript
关于vue中watch检测到不到对象属性的变化的解决方法
2018/02/08 Javascript
Echart折线图手柄触发事件示例详解
2018/12/16 Javascript
vue项目设置scrollTop不起作用(总结)
2018/12/21 Javascript
JS 实现发送短信验证码的“59秒后重新发送验证短信”功能
2019/08/23 Javascript
Vue实现手机号、验证码登录(60s禁用倒计时)
2020/12/19 Vue.js
Python实现备份文件实例
2014/09/16 Python
pymongo实现控制mongodb中数字字段做加法的方法
2015/03/26 Python
在Python中操作日期和时间之gmtime()方法的使用
2015/05/22 Python
Python实现读取文件最后n行的方法
2017/02/23 Python
利用Python+Java调用Shell脚本时的死锁陷阱详解
2018/01/24 Python
关于pymysql模块的使用以及代码详解
2019/09/01 Python
vscode写python时的代码错误提醒和自动格式化的方法
2020/05/07 Python
法国最大的在线眼镜店:EasyLunettes
2019/08/26 全球购物
Helly Hansen工作服美国官方网上商店:为最恶劣的环境
2019/09/04 全球购物
英国领先的电动可调床制造商:Laybrook
2019/12/26 全球购物
爱心募捐通知范文
2015/04/27 职场文书
员工手册董事长致辞
2015/07/29 职场文书
公司转让协议书
2016/03/19 职场文书
Spring boot admin 服务监控利器详解
2022/08/05 Java/Android