资讯专栏INFORMATION COLUMN

长图文加载优化

shiyang6017 / 2717人阅读

摘要:最近在开发一个长图文预览项目,主要用在手机端浏览主要在微信端。大概的实现是首屏开始显示接下来首屏后面的图片就全部扔给浏览器去加载了。

最近在开发一个长图文预览项目,主要用在手机端浏览(主要在微信端)。这项目其实就是一个手机网页,把数据中的文本和图片等元素渲染出来即可。这样的项目很常见,包括微信内公众号的文章etc.这个项目很简单,但非常头疼的一个问题是对图片的懒加载处理。(下面讨论的加载策略暂且都是针对图片

前置条件:假定项目数据是一个数组,数组元素都是图片,并且指定了图片在屏幕中的left和top。

我们最开始想到的处理方式是:优先考虑首屏体验。
取得数据后,首屏先呈现Loading状态。通过屏幕的高度H和图片的top,得到首屏的图片,并对其每一个图片的onload和onerror事件绑定回调。当回调全都执行完成之后便将首屏Loading状态移除,呈现首屏的图片。大概的实现是:

    firstScreenPromises = firstScreenImgs.map((img) => {
      return new Promise((resolve, reject) => {
        let image = new Image()
        image.src = img.imgSrc
        image.onload = image.onerror = resolve
      })
    })
    Promise.all(firstScreenPromises).then(data => {
      // 首屏开始显示
    })

接下来首屏后面的图片就全部扔给浏览器去加载了。

上面首屏显示优化自然ok,不过对后面屏幕图片的显示策略自然是不太好的。于是考虑分屏加载,一屏一屏加载图片。

    // 分屏
    screenBox = {}
    H = screen.height
    
    for (let i = 0; i < imgs.length; i += 1) {
      screenNum = Math.floor(imgs.top / H)
      screenBox[screenNum] = screenBox[screenNum] || []
      screenBox[screenNum].push(imgs[i])
    }
    
    indexs = Object.keys(screenBox)
    ----------上边代码块 (X) 继续给下一块代码使用——------------
    loadNext()
    
    // 分屏按序加载
    function loadNext() {
      if (!indexs.length)
        return
    
      screenBox[indexs[0]].map(img=>{
        // 同首屏firstScreenPromises
    
      }).then(data=>{
        // load完后回调该函数继续load下一屏
        indexs.shift()
        loadNext()
      })
    }

上面优化点在于按序分屏加载图片,这适合用户慢慢往下看的情况,但是会有两个弊端。
一是:如果用户突然猛翻到页面较后的位置,此时如果还在加载前面某屏的图片,那用户需要等待。
二是:如果页面图片元素非常多,屏幕数很多,会消耗许多流量,也许用户不想看到最后咧。

于是考虑控制预加载的屏幕数,用户看屏幕所时处的屏幕数为N,预加载N+1,N+2....N+M,最多预加载M屏。同时监听屏幕滚动,通过滚动的高度算出用户所处的屏幕数。

    ....上边代码块(X).....
    loadNext(0) // 初始化load首屏
    window.onscroll = ()=>{
      getViewScreenIndex = function() {
        // 滚动监听求出用户视野所处的屏幕数(若处在a,a+1,取a)
        return ...
      }
      loadNext(getViewScreenIndex())
    }
    // 加载第N屏,加载完继续加载到第N+M屏
    function loadNext(N) {
      hasLoad = 0
      function load() {
        if (hasLoad == M) {
          // 已经加载到N+M屏,停止预加载
          return
        }
    
        hasLoad++
    
        if (indexs.indexOf(N) > -1) {
          screenBox[indexs[0]].map(img=>{
            // 同首屏firstScreenPromises
    
          }).then(data=>{
            // load完后回调该函数继续load下一屏
            indexs.splice(indexs.indexOf(N), 1)
            load()
          })
        } else {
          load()
        }    
      }
    }
        

最后一个优化点:假设正在load第1屏(接下来会预加载第2,3屏),此时屏幕滚动到了第4屏(将会预加载第5,6屏)。此时是否还有必要继续去加载第2,3屏?我觉得是没必要的,用户更有可能会继续往后翻。所以此时我会取消掉2,3屏的预加载(当然,如果此时正在预加载第2屏,那只会取消掉第3屏的加载)。
这一块的代码,加上一些细节处理,可以到我的github lazyloader看看。

总结:目前能考虑到的上边策略为的是提升用户体验(预加载),同时不会去消耗太多流量(限制预加载的数目)。但我相信还会有更加优化的策略,希望能得到高人的指点,那就真的灰常感激啦!
同时,这里边还会遇到一些兼容上的坑。比如:此处我所用到的滚动监听是window.onscroll。这个监听事件在不同设备上的表现非常不一样,会使得这里的加载策略不一定能使所有的设备都体验不错。
android的(不确定是不是所有)window.onscroll会在手指按着屏幕拖动时触发,以及屏幕滚动停止的时候触发;而ios的则是只在屏幕停止滚动的时候才会触发。这两者,在松开手后屏幕滚动时都不会触发onscroll事件。
目前还没想到比较好的兼容策略,希望有人能提供好的资料和想法借鉴借鉴,感激涕零。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80189.html

相关文章

  • 浅说虚拟列表的实现原理

    摘要:虚拟列表的实现有多种方案,本文以组件为基础进行分析。常见的无限滚动便是延迟渲染的一种实现,而虚拟列表则是按需渲染的一种实现。接下来,本文会简单介绍虚拟列表的一种实现方案。实现本章节将会创建一个组件,并结合代码,慢慢梳理虚拟列表的实现。 在 列表数据的展示优化 一文中,提到了对于列表形态的数据展示的按需渲染。这种方式是指根据容器元素的高度以及列表项元素的高度来显示长列表数据中的某一个部分...

    赵春朋 评论0 收藏0
  • 商品详情页上拉查看详情

    摘要:商品详情页上拉查看详情目录介绍该库介绍效果展示如何使用注意要点优化问题部分代码逻辑参考案例该库介绍模仿淘宝京东考拉等商品详情页分页加载的效果。 商品详情页上拉查看详情 目录介绍 01.该库介绍 02.效果展示 03.如何使用 04.注意要点 05.优化问题 06.部分代码逻辑 07.参考案例 01.该库介绍 模仿淘宝、京东、考拉等商品详情页分页加载的UI效果。可以嵌套Recycl...

    Apollo 评论0 收藏0
  • 图文并茂揭开深度学习神秘面纱,兼谈人工智能狂热的荒诞

    摘要:对深度学习模型而言,水就是海量的数据。就拿机器识别物体这样的任务来说,通过数百万副图片的训练,深度学习模型甚至可以超过人的肉眼的识别能力,这确实是人工智能在感知类问题上重要的里程碑。关于深度学习,还有一个有趣的现象。 说到人工智能和机器人,上点儿岁数的码农们可能对封面这张图有点印象。不明就里的朋友,可以回去补习一下《编辑部的故事》。我是个二手的人工智能表演艺术家:从博士毕业开始,就在MSRA...

    jimhs 评论0 收藏0

发表评论

0条评论

shiyang6017

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<