小编给大家分享一下如何使用react-virtualized实现图片动态高度长列表,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!
虚拟列表是一种根据滚动容器元素的可视区域来渲染长列表数据中某一个部分数据的技术。虚拟列表是对长列表场景一种常见的优化,毕竟很少有人在列表中渲染上百个子元素,只需要在滚动条横向或纵向滚动时将可视区域内的元素渲染出即可。
开发中遇到的问题
1.长列表中的图片要保持原图片相同的比例,那纵向滚动在宽度不变的情况下,每张图片的高度就是动态的,当该列表项高度发生了变化,会影响该列表项及其之后所有列表项的位置信息。
2.图片width,height必须在图片加载完成后才能获得.
解决方案
我们使用react-virtualized中list组件,官方给出的例子
importReactfrom'react';
importReactDOMfrom'react-dom';
import{List}from'react-virtualized';
//Listdataasanarrayofstrings
constlist=[
'BrianVaughn',
//Andsoon...
];
functionrowRenderer({
key,//Uniquekeywithinarrayofrows
index,//Indexofrowwithincollection
isScrolling,//TheListiscurrentlybeingscrolled
isVisible,//ThisrowisvisiblewithintheList(egitisnotanoverscannedrow)
style,//Styleobjecttobeappliedtorow(topositionit)
}){
return(
{list[index]}
);
}
//Renderyourlist
ReactDOM.render(
,
document.getElementById('example'),
);
其中rowHeight是每一行的高度,可以传入固定高度也可以传入function。每次子元素高度改变需要调用recomputeRowHeights方法,指定索引后重新计算行高度和偏移量。
具体实现
constImgHeightComponent=({imgUrl,onHeightReady,height,width})=>{
const[style,setStyle]=useState({
height,
width,
display:'block',
})
constgetImgWithAndHeight=(url)=>{
returnnewPromise((resolve,reject)=>{
varimg=newImage()
//改变图片的src
img.src=url
letset=null
constonload=()=>{
if(img.width||img.height){
//图片加载完成
clearInterval(set)
resolve({width:img.width,height:img.height})
}
}
set=setInterval(onload,40)
})
}
useEffect(()=>{
getImgWithAndHeight(imgUrl).then((size)=>{
constcurrentHeight=size.height*(width/size.width)
setStyle({
height:currentHeight,
width:width,
display:'block',
})
onHeightReady(currentHeight)
})
},[])
return
}
先写一个获取图片高度的组件,通过定时循环检测获取并计算出高度传给父组件。
importReact,{useState,useEffect,useRef}from'react'
importstylesfrom'./index.scss'
import{AutoSizer}from'react-virtualized/dist/commonjs/AutoSizer'
import{List}from'react-virtualized/dist/commonjs/List'
exportdefaultclassDocumentStudyextendsReact.Component{
constructor(props){
super(props)
this.state={
list:[],
heights:[],
autoWidth:900,
autoHeight:300
}
}
handleHeightReady=(height,index)=>{
this.setState(
(state)=>{
constflag=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})=>{
constrow=this.state.heights.find((item)=>item.index===index)
returnrow?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(