使用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函数ajax
Aug 20 Javascript
js和jquery如何获取图片真实的宽度和高度
Sep 28 Javascript
纯JS实现旋转图片3D展示效果
Apr 12 Javascript
jQuery结合ajax实现动态加载文本内容
May 19 Javascript
跟我学习javascript的for循环和for...in循环
Nov 18 Javascript
JQuery日历插件My97DatePicker日期范围限制
Jan 20 Javascript
全面解析JavaScript的Backbone.js框架中的Router路由
May 05 Javascript
图解Javascript——作用域、作用域链、闭包
Mar 21 Javascript
javascript基于定时器实现进度条功能实例
Oct 13 Javascript
vue使用rem实现 移动端屏幕适配
Sep 26 Javascript
vue.js实现的幻灯片功能示例
Jan 18 Javascript
jquery3和layui冲突导致使用layui.layer.full弹出全屏iframe窗口时高度152px问题
May 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
根德Grundig S400/S500/S700电路分析
2021/03/02 无线电
PHP中显示格式化的用户输入
2006/10/09 PHP
PHP 各种排序算法实现代码
2009/08/20 PHP
一个非常实用的php文件上传类
2017/07/04 PHP
javascript this用法小结
2008/12/19 Javascript
理解JavaScript变量作用域更轻松
2009/10/25 Javascript
jQuery选择器的工作原理和优化分析
2011/07/25 Javascript
纯javascript实现简单下拉刷新功能
2015/03/13 Javascript
JS打印组合功能
2016/08/04 Javascript
Bootstrap 3的box-sizing样式导致UEditor控件的图片无法正常缩放的解决方案
2016/09/15 Javascript
jQuery Masonry瀑布流布局神器使用详解
2017/05/25 jQuery
javascript实现Java中的Map对象功能的实例详解
2017/08/21 Javascript
vue使用keep-alive实现数据缓存不刷新
2017/10/21 Javascript
vue.js系列中的vue-fontawesome使用
2018/02/10 Javascript
Vue 父子组件的数据传递、修改和更新方法
2018/03/01 Javascript
微信小程序 slot踩坑的解决
2019/04/01 Javascript
详解Vue组件之间通信的七种方式
2019/04/14 Javascript
js实现指定时间倒计时效果
2019/08/26 Javascript
js实现简单掷骰子效果
2019/10/24 Javascript
Vue实现点击导航栏当前标签后变色功能
2020/08/19 Javascript
使用numba对Python运算加速的方法
2018/10/15 Python
python3实现微型的web服务器
2019/09/03 Python
python有序查找算法 二分法实例解析
2020/02/18 Python
Django Serializer HiddenField隐藏字段实例
2020/03/31 Python
Python基于smtplib协议实现发送邮件
2020/06/03 Python
Python Opencv实现单目标检测的示例代码
2020/09/08 Python
python如何利用Mitmproxy抓包
2020/10/10 Python
Python大批量搜索引擎图像爬虫工具详解
2020/11/16 Python
css3新增颜色表示方式分享
2014/04/15 HTML / CSS
美国药妆网站:EDCskincare.com(防晒、痤疮、抗衰老等)
2017/04/28 全球购物
党员年终民主评议的自我评价
2013/11/05 职场文书
本科应届生自荐信
2014/06/29 职场文书
聘任协议书(挂靠)
2015/09/21 职场文书
初中政治教师教学反思
2016/02/23 职场文书
送给教师们,到底该如何写好教学反思?
2019/07/02 职场文书
win10搭建配置ftp服务器的方法
2022/08/05 Servers