V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
skyzt
V2EX  ›  程序员

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

  •  
  •   skyzt · 2022-06-10 14:48:07 +08:00 · 1267 次点击
    这是一个创建于 889 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我正在使用 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 条回复    2022-06-11 09:36:38 +08:00
    tsanie
        1
    tsanie  
       2022-06-10 15:04:35 +08:00
    用 setTimeout 吧,否则 UI 刷新要等到同步方法执行完成后才会触发。
    !function (e) { setTimeout(() => handleCanvasUpdate(e), 0) }(element)
    MegrezZhu
        2
    MegrezZhu  
       2022-06-10 15:08:00 +08:00   ❤️ 1
    看能不能用 worker 把运算量放到后台线程去
    thinkershare
        3
    thinkershare  
       2022-06-10 15:11:13 +08:00
    将所有重度运算移动到 Web Worker 中去, 不要再浏览器的主线程上做 CPU 重度操作.
    oldshensheep
        4
    oldshensheep  
       2022-06-10 15:39:25 +08:00
    Web Workers

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

    其实如果有更高的需求建议使用 WebGL
    codehz
        5
    codehz  
       2022-06-11 09:36:38 +08:00
    (worker 里也可以跑 canvas ,transferControlToOffscreen 到 worker 之后就可以直接画了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5320 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 07:26 · PVG 15:26 · LAX 23:26 · JFK 02:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.