使用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的event详解。
Sep 06 Javascript
JavaScript URL参数读取改进版
Jan 16 Javascript
Javascript结合css实现网页换肤功能
Nov 02 Javascript
js预载入和JavaScript Image()对象使用介绍
Aug 28 Javascript
常见JS效果之图片减速度滚动实现代码
Dec 08 Javascript
jquery动画1.加载指示器
Aug 24 Javascript
禁止IE用右键的JS代码
Dec 30 Javascript
javascript中的面向对象
Mar 30 Javascript
WdatePicker.js时间日期插件的使用方法
Jul 26 Javascript
在Vue中使用highCharts绘制3d饼图的方法
Feb 08 Javascript
解决包含在label标签下的checkbox在ie8及以下版本点击事件无效果兼容的问题
Oct 27 Javascript
用jQuery实现抽奖程序
Apr 12 jQuery
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编程效率的方法
2013/11/07 PHP
php中 $$str 中 &quot;$$&quot; 的详解
2015/07/06 PHP
session 加入redis的实现代码
2016/07/15 PHP
在Mac OS下搭建LNMP开发环境的步骤详解
2017/03/10 PHP
php+mysql+ajax 局部刷新点赞/取消点赞功能(每个账号只点赞一次)
2020/07/24 PHP
JavaScript 学习笔记(七)字符串的连接
2009/12/31 Javascript
15条JavaScript最佳实践小结
2013/08/09 Javascript
js获取日期:昨天今天和明天、后天
2014/06/11 Javascript
javascript获取元素离文档各边距离的方法
2015/02/13 Javascript
jQuery实现折叠、展开的菜单组效果代码
2015/09/16 Javascript
使用node+vue.js实现SPA应用
2016/01/28 Javascript
jQuery+Ajax实现限制查询间隔的方法
2016/06/07 Javascript
理解nodejs的stream和pipe机制的原理和实现
2017/08/12 NodeJs
vue中使用iview自定义验证关键词输入框问题及解决方法
2018/03/26 Javascript
Python显示进度条的方法
2014/09/20 Python
python判断图片宽度和高度后删除图片的方法
2015/05/22 Python
python中利用await关键字如何等待Future对象完成详解
2017/09/07 Python
python实现百万答题自动百度搜索答案
2018/01/16 Python
opencv python 2D直方图的示例代码
2018/07/20 Python
程序员写Python时的5个坏习惯,你有几条?
2018/11/26 Python
python游戏开发之视频转彩色字符动画
2019/04/26 Python
Python猴子补丁Monkey Patch用法实例解析
2020/03/23 Python
Python实现在线批量美颜功能过程解析
2020/06/10 Python
浅谈Python3中print函数的换行
2020/08/05 Python
详解Django自定义图片和文件上传路径(upload_to)的2种方式
2020/12/01 Python
将SVG图引入到HTML页面的实现
2019/09/20 HTML / CSS
阿拉伯世界最大的电子卖场:Souq埃及
2016/08/01 全球购物
协议书样本
2014/04/23 职场文书
建筑结构施工求职信
2014/07/11 职场文书
人事专员岗位职责说明书
2014/07/30 职场文书
群众路线教师自我剖析材料
2014/09/29 职场文书
惊涛骇浪观后感
2015/06/05 职场文书
2016元旦晚会主持人开场白和结束语
2015/12/03 职场文书
学习心理学心得体会
2016/01/22 职场文书
Oracle 区块链表创建过程详解
2021/05/15 Oracle
Golang 实现 WebSockets 之创建 WebSockets
2022/04/24 Golang