• 请不要在回答技术问题时复制粘贴 AI 生成的内容
skyzt
V2EX  ›  程序员

[急!在线等] JS 怎么在处理多个大图数据的时候避免线程阻塞?

  •  
  •   skyzt · Jun 10, 2022 · 1930 views
    This topic created in 1444 days ago, the information mentioned may be changed or developed.

    我正在使用 PanoJS 创建一个可以查看和修改巨大图像的平台。

    我需要做的是用 canvas 渲染多个 图像切片,并且能改变图像的亮度,对比度,不透明度。

    但问题是,当我运行这个 updateTiles 函数时,切片 没有被逐个更新,它们会等待所有的 tiles 被更新以后一起渲染,图像大了以后要卡住好几秒,一开始设置的 loading 透明度也在所有切片都更新以后才会生效。

    希望有了解这方面的大神指点一下,多谢!

    ...
    
    PanoJS.prototype.updateTiles = function () {
    
      const handleCanvasUpdate = (element) => {
        const ctx = element.getContext('2d')
        const imgData = new ImageData(new Uint8ClampedArray(JSON.parse(element._imgData.data)), element._imgData.height, element._imgData.width)
        const changedImgData = this.changeImgData(imgData) // -1 ~ 1
        ctx.putImageData(changedImgData, 0, 0);
      }
    
      const tag = this.showLoading('updateTiles')
      for (let i = 0; i < this.well.children.length; i++) {
        const element = this.well.children[i];
        if (element.tagName.toUpperCase() === 'CANVAS') {
          handleCanvasUpdate(element)
        }
      }
      this.hideLoading(tag)
    }
    
    PanoJS.prototype.changeImgData = function (imgdata) {
    
      // this imgdata can be very large
    
      const data = imgdata.data;
      for (let i = 0; i < data.length; i += 4) {
    
        // brightness
        const hsv = this.rgb2hsv([data[i], data[i + 1], data[i + 2]]);
        hsv[2] *= 1 + this.luminance;
        const rgb = this.hsv2rgb([...hsv]);
        data[i] = rgb[0];
        data[i + 1] = rgb[1];
        data[i + 2] = rgb[2];
    
        // contrast
        const _contrast = (this.contrast / 100) + 1;  //convert to decimal & shift range: [0..2]
        const intercept = 128 * (1 - _contrast);
        data[i] = data[i] * _contrast + intercept;
        data[i + 1] = data[i + 1] * _contrast + intercept;
        data[i + 2] = data[i + 2] * _contrast + intercept;
    
        // opacity
        data[i + 3] = data[i + 3] * this.opacity;
    
      }
      return imgdata;
    }
    
    
    PanoJS.prototype.showLoading = function (name) {
      counter++
      const tag = `${counter}${name}`
      console.time(tag)
      this.loadingMask.style.opacity = 1
      this.loadingCount++
      return tag
    }
    
    PanoJS.prototype.hideLoading = function (tag) {
      // requestAnimationFrame(() => {
      if (this.loadingCount) {
        this.loadingCount--
      }
      // console.log(this.loadingMask.style.opacity);
      if (this.loadingCount === 0) {
        this.loadingMask.style.opacity = 0
      }
      console.timeEnd(tag)
      // console.log(this.loadingCount);
      // })
    }
    ...
    
    5 replies    2022-06-11 09:36:38 +08:00
    tsanie
        1
    tsanie  
       Jun 10, 2022
    用 setTimeout 吧,否则 UI 刷新要等到同步方法执行完成后才会触发。
    !function (e) { setTimeout(() => handleCanvasUpdate(e), 0) }(element)
    MegrezZhu
        2
    MegrezZhu  
       Jun 10, 2022   ❤️ 1
    看能不能用 worker 把运算量放到后台线程去
    thinkershare
        3
    thinkershare  
       Jun 10, 2022
    将所有重度运算移动到 Web Worker 中去, 不要再浏览器的主线程上做 CPU 重度操作.
    oldshensheep
        4
    oldshensheep  
       Jun 10, 2022
    Web Workers

    我这里有一个简单的图像处理程序,没有使用第三方处理图像的库,使用了 Web Workers 你可以参考一下。
    https://blog.oldshensheep.com/toyweb/dip/
    这里是源码 https://github.com/oldshensheep/toyweb/tree/main/dip
    之前我也是图片大了就卡住了,换成 Web Worker 就没事了。

    其实如果有更高的需求建议使用 WebGL
    codehz
        5
    codehz  
       Jun 11, 2022
    (worker 里也可以跑 canvas ,transferControlToOffscreen 到 worker 之后就可以直接画了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   972 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 22:53 · PVG 06:53 · LAX 15:53 · JFK 18:53
    ♥ Do have faith in what you're doing.