懒加载之几百张图片

网页要展示很多张图片 手机性能不够,渲染过多图片容易造成页面卡死 甚至页面奔溃。。。

 

最近做项目的时候就遇到了这样的问题。

后端将所有URL链接都返回过来,如果直接循环渲染到img标签里面,大于几十张有些安卓机子就会死机

 

所以必须考虑用到懒加载,即 先不渲染所有URL链接,滚动到可视区域范围,再去渲染后面的URL,这样操作即使几百张图片在苹果机子也是没问题的

我这里用的是react hooks的解法,其他可模仿思路。

 

(一)

默认先加载几页,保证会有滚动条的情况,

然后向下滚动,滚动到滚动条到底可加载下一页

 

包裹图片的外框我称为 box

box的全部总高(scrollHeight) -  box可见区域高(clientHeight) <=  box滚动条的高度(scrollTop)

(因为我的项目中图片大小是A4纸大小,所以默认显示的页数是2页,2页就可以有滚动条了,可以按照实际情况来定义默认页数)

const [pageIdx, setPageIdx] = useState(0) // 当前页数

useEffect(() => {
    setPageIdx(2)
}, [])

const handleScroll = () => { // 滚动事件:滚动加载
    if (pageIdx < contentImgs.length - 1 && (myref.current.scrollHeight - myref.current.clientHeight) <= myref.current.scrollTop) {
      setPageIdx(pageIdx + 1)
    }
}

// contentImgs 所有的图片链接
const renderContent = () => { // 正文部分 
    return contentImgs.slice(0, pageIdx+1).map(f => <img src={f} key={f} />) 
}

 

这个方法比较简单了,但是毕竟随着往下滚动 渲染的越来越多 终究支撑不住

那么就得考虑永远只渲染10页的情况

 

(二)

 只渲染10页,需要 不仅要考虑到上拉加载,也要考虑下拉加载, 因为每10页都得重新加载

这还得考虑方向,是向上还是向下,上拉的话pageNum要增加 下拉要减少

let endToast = false; // 提示结束 避免反复刷
const myBox = document.getElementById('myInput')
const pageH = (1123 + 10) * scale; // 每页的高度

//懒加载处理
const [pageIdx, setPageIdx] = useState(0) // 当前页数
const [imgs, setImgs] = useState() // 当前展示的图片链接
const [startY, setStartY] = useState(0) // 触摸开始位置(用于判断滚动方向)
const [endY, setEndY] = useState(0) // 触摸结束位置(用于判断滚动方向)

 
useEffect(() => {
    if (!myBox) return;

    if (contentImgs.length > 100) {
      setPageIdx(10)
      const imgsList = contentImgs.slice(0, 10)
      const curImg = []
      curImg.push(...imgsList)
      setImgs(curImg)

      myBox.ontouchstart = function(e) {
        const pageY = e.changedTouches[0].pageY
        setStartY(pageY)
      }

      myBox.ontouchend = function(e) {
        const pageY = e.changedTouches[0].pageY
        setEndY(pageY)
      }
    } else {
      setPageIdx(3) // 小于100页 默认展示3页
    }
  }, [contentImgs])

 useEffect(() => {
    if (contentImgs.length <= 100) return;
    // 上拉,下滑
    if (endY < startY) {
      const totalHeight = myBox.clientHeight + myBox.scrollTop
      if (totalHeight === myBox.scrollHeight && !endToast) {
        if (pageIdx >= contentImgs.length) {
          endToast = true;
          Toast.loading('加载完毕', 2);
          return
        }

        Toast.loading('正在加载...', 2);
        myBox.scrollTop = 0;
        setPageIdx(pageIdx + 10)
        const imgsList = contentImgs.slice(pageIdx, pageIdx + 10)
        setImgs([...imgsList])
      }
    }

    // 下拉,上滑
    if (endY > startY && myBox.scrollTop === 0 && pageIdx !== 10) {
      endToast = false;
      Toast.loading('正在加载...', 2);
      myBox.scrollTop = pageH * 10;
      setPageIdx(pageIdx - 10)
      const imgsList = contentImgs.slice(pageIdx - 20, pageIdx - 10)
      setImgs([...imgsList])
    }
  }, [startY, endY])


const renderContent = () => { // 正文部分
    return imgs.map((f, i) => <img src={f} key={i} />)
  }

 

但是方案二 会有弊端 因为是分段展示,下拉的时候 会出现从第20页跳到10页再到19页 即使有loading效果 还是会有个跳动的过程,可以优化下

 

有小伙伴刷到 如果更好的办法,欢迎留言,希望可以不吝赐教

 

posted @ 2020-08-25 17:30  水晴  阅读(245)  评论(0)    收藏  举报