使用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 相关文章推荐
ASP小贴士/ASP Tips javascript tips可以当桌面
Dec 10 Javascript
浅析jquery某一元素重复绑定的问题
Jan 03 Javascript
JQuery报错Uncaught TypeError: Illegal invocation的处理方法
Mar 13 Javascript
详细分析使用AngularJS编程中提交表单的方式
Jun 19 Javascript
javascript获取当前的时间戳的方法汇总
Jul 26 Javascript
jquery实现滑动特效代码
Aug 10 Javascript
js+flash实现的5图变换效果广告代码(附演示与demo源码下载)
Apr 01 Javascript
Knockoutjs 学习系列(一)ko初体验
Jun 07 Javascript
jquery根据name取得select选中的值实例(超简单)
Jan 25 jQuery
js技巧之十几行的代码实现vue.watch代码
Jun 09 Javascript
一文读懂ES7中的javascript修饰器
May 06 Javascript
jquery制作的移动端购物车效果完整示例
Feb 24 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来处理多个提交任务
2008/05/08 PHP
php设计模式 Singleton(单例模式)
2011/06/26 PHP
php中的四舍五入函数代码(floor函数、ceil函数、round与intval)
2014/07/14 PHP
利用php-cli和任务计划实现订单同步功能的方法
2017/05/03 PHP
php+laravel依赖注入知识点总结
2019/11/04 PHP
解决Laravel5.x的php artisan migrate数据库迁移创建操作报错SQLSTATE[42000]
2020/04/06 PHP
javascript jQuery $.post $.ajax用法
2008/07/09 Javascript
js 页面输出值
2008/11/30 Javascript
jquery获取input表单值的代码
2010/04/19 Javascript
IE6背景图片不缓存问题解决方案及图片使用策略多个方法小结
2012/05/14 Javascript
关于eval 与new Function 到底该选哪个?
2013/04/17 Javascript
JavaScript获取和设置CheckBox状态的简单方法
2013/07/05 Javascript
js如何获取兄弟、父类等节点
2014/01/06 Javascript
javascript异步编程的4种方法
2014/02/19 Javascript
JavaScript程序开发之JS代码放置的位置
2016/01/15 Javascript
js实现获取两个日期之间所有日期的方法
2016/06/17 Javascript
vue实现图片滚动的示例代码(类似走马灯效果)
2018/03/03 Javascript
vue中Npm run build 根据环境传递参数方法来打包不同域名
2018/03/29 Javascript
JS将网址url转化为JSON格式的方法
2018/07/02 Javascript
详解Vue项目中出现Loading chunk {n} failed问题的解决方法
2018/09/14 Javascript
react-router 路由切换动画的实现示例
2018/12/03 Javascript
JavaScript实现背景自动切换小案例
2019/09/27 Javascript
VUEX采坑之路之获取不到$store的解决方法
2019/11/08 Javascript
node.JS事件机制与events事件模块的使用方法详解
2020/02/06 Javascript
通过实例了解Render Props回调地狱解决方案
2020/11/04 Javascript
js实现日历
2020/11/07 Javascript
解决vue elementUI 使用el-select 时 change事件的触发问题
2020/11/17 Vue.js
Python编程中使用Pillow来处理图像的基础教程
2015/11/20 Python
Java实现的执行python脚本工具类示例【使用jython.jar】
2018/03/29 Python
Django实现跨域的2种方法
2019/07/31 Python
Python3 翻转二叉树的实现
2019/09/30 Python
python opencv图片编码为h264文件的实例
2019/12/12 Python
PyTorch实现重写/改写Dataset并载入Dataloader
2020/07/14 Python
爱他美官方海外旗舰店:Aptamil奶粉
2017/12/22 全球购物
青年岗位能手事迹材料(2016推荐版)
2016/03/01 职场文书
关于mysql中时间日期类型和字符串类型的选择
2021/11/27 MySQL