Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

显示1000张图片合集时的性能优化建议和BUG #547

Open
xiaosongmao123 opened this issue May 16, 2022 · 1 comment
Open

显示1000张图片合集时的性能优化建议和BUG #547

xiaosongmao123 opened this issue May 16, 2022 · 1 comment

Comments

@xiaosongmao123
Copy link

很好的项目,感谢你的工作

Describe the bug
当我想要用viewerjs去显示图片合集时(无法预估数量,所以要尝试最大值1000张)。发现性能很差。

然后就分析了一下,发现,性能差完全是viewerjs设计问题+BUG,而不是我的需求--显示1000张图片不对。

To Reproduce
Steps to reproduce the behavior:
1.创建1000张图片的合集

 <ul id="images" style={{ display: 'none' }}>

                  {showlist.map((item) => (
                    <li key={item.fileindex}>

                      <img src={item.src} data-src={item.datasource} referrerPolicy="no-referrer" />

                    </li>
                  ))}

</ul>

2.初始化viewjs

this.viewver = new Viewer(document.getElementById('images')!, {...})

3.点击幻灯片播放

Expected behavior
预期正常展现。实际很差,很卡

Additional context

我发现的问题比较多,我没有按照重要程度去排序,所以请耐心看一下。都是为了让Viewer更完美


问题1: viewerjs强制要求必须用img列表去实例化new Viewer(),带来了不必要的消耗

在我的用例中,ul>li>img (1000张),它实际上并没有实际意义。因为Viewer在实例化时,是Viewer自己重新创建了1000个新的img并复制原始数据

//https://github.com/fengyuanchen/viewerjs/blob/main/src/js/render.js  line 82
forEach(this.images, (image, index) => {
      const { src } = image;
      const alt = image.alt || getImageNameFromURL(src);
      const url = this.getImageURL(image);

      if (src || url) {
        const item = document.createElement('li');
        const img = document.createElement('img');

        forEach(options.inheritedAttributes, (name) => {
          const value = image.getAttribute(name);

          if (value !== null) {
            img.setAttribute(name, value);
          }
        });

        img.src = src || url;
        img.alt = alt;

如上,那么最开始创建的ul>li>img (1000张),实际上完全可以创建为 ul>li>span (1000个) 来代替。因为Viewer只是需要读取复制数据,在span上设置这些数据给Viewer读取也是一样的<span src={item.src} data-src={item.datasource} referrerPolicy="no-referrer" />。这样我最开始不需要创建和加载1000个img (导致网络卡)

这个问题,我自己使用替代图片的方式优化了一下(使用一张很小的固定图片替换了img 的 src)(在Viewer的view: (e: any)事件中还有处理替换回来)


 <ul id="images" style={{ display: 'none' }}>
                  {showlist.map((item) => (
                    <li key={item.fileindex}>

                       //图片占位符替换
                      <img src="data:image/png;base64,iVBORw0..." data-imgsrc={item.src}  data-src={item.datasource} referrerPolicy="no-referrer" />

                    </li>
                  ))}
</ul>

问题2: Viewer一次性创建了1000个新的img,导致第一次展现时,需要加载1000张图片,网络导致很卡

还是如上//https://github.com/fengyuanchen/viewerjs/blob/main/src/js/render.js line 82
Viewer一次性创建了1000个新的img,这些img是被用来放在viewer-navbar做导航用的

//Viewer最终渲染的html

<div class="viewer-container">
<div class="viewer-navbar">
<ul class="viewer-list viewer-transition" role="navigation" style="width: 30906px; transform: translateX(307.5px);">

<li data-index="0" data-viewer-action="view" role="button" tabindex="0" class="">
//1000张小图片
<img  src="...” />
</li>
......
</ul>
</div>
</div>

创建1000个?感觉不合理,因为导航条放不下1000个,应该先创建一部分,当切换下一张图片,导航接近时,再补充、替换一部分。这样循环

或者,创建1000个img,但是不要复制真实的src,而是在导航接近时,更新需要显示的那些img的src,让导航图片正常显示

针对这个问题,我是在Viewer的view: (e: any)事件中还有处理替换回来。这样,仅保持几十个img有真实的src,其他的只是占位。可以避免加载1000张图片带来的网络卡顿

viewver = new Viewer(document.getElementById('images')!, {           
            url: 'data-src',
            view: (e: any) => {
              const nextindex = e.detail.index /** 马上要显示的图片的id */
              let imagelist = rawimagelist.value
              let rawimg = imagelist[nextindex]
             
              let ul = document.getElementsByClassName('viewer-list viewer-transition')
              if (ul && ul.length > 0) {
                let lilist = ul[0].childNodes
                /** 找到当前显示的图片,更新前后30个li>img 的src */
                for (let i = Math.max(0, nextindex - 30), maxi = Math.min(nextindex + 30, imagelist.length); i < maxi; i++) {
                  let imgnode = lilist[i].firstChild
                  if (imgnode) {
                    let img = imgnode as Element
                    let src = img.getAttribute('src')
                    let smallurl = imagelist[i].smallurl
                    if (src != smallurl) img.setAttribute('src', smallurl)
                    src = img.getAttribute('data-original-url')
                    let bigurl = imagelist[i].bigurl
                    if (src != bigurl) img.setAttribute('data-original-url', bigurl)
                  }
                }
              }
            }
          })
        }

问题3: 点击幻灯片播放时会卡住

原因是,点击播放时,Viewer会创建1000个大图的img

这里有个BUG了,因为这1000张大图是放在viewer-player里播放的,但我竟然在body下还有1000个,这应该是BUG了

<body>
    <div class="viewer-container">
        <div class="viewer-player viewer-show" >
            <img .../>
            ....1000...//应该是放在这里,循环播放的
        </div>
</div>
<img .../>
....1000...//但是在body下竟然还有1000张?
</body>

针对这个问题,还是那个思路,当播放时,播放到第几张图片,就同时替换更新邻近的几张图片的src。而不是一点击播放按钮就创建1000张图片并发起1000个网络请求


总结,Viewer没有对大量图片的情景进行优化。问题3可能是BUG,我没时间翻Viewer的代码仔细看了

请不要回复一句:这可能需要修改Viewer的架构了。。。。

其实并不是这样的,最简易的就是使用占位图片。先占位。然后等临近显示时更新替换为正确的图片。这样即使创建1000个img,也不会真的立即联网加载1000张图片,导致网络太卡。

当然最合理的方式,还是修改一下显示逻辑,仅创建几十个必要的img,然后等临近显示时更新这几十个img的图片地址。这样不需要创建1000个img(现在实际上是创建了3000个img,并且立即一次性加载1000张图片).

@fengyuanchen
Copy link
Owner

目前 v1 版本暂时这样了。如果后面有时间做 v2 版本,可以重构一下。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants