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

2022-06-10 14:48:07 +08:00
 skyzt

我正在使用 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);
  // })
}
...
1266 次点击
所在节点    程序员
5 条回复
tsanie
2022-06-10 15:04:35 +08:00
用 setTimeout 吧,否则 UI 刷新要等到同步方法执行完成后才会触发。
!function (e) { setTimeout(() => handleCanvasUpdate(e), 0) }(element)
MegrezZhu
2022-06-10 15:08:00 +08:00
看能不能用 worker 把运算量放到后台线程去
thinkershare
2022-06-10 15:11:13 +08:00
将所有重度运算移动到 Web Worker 中去, 不要再浏览器的主线程上做 CPU 重度操作.
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
2022-06-11 09:36:38 +08:00
(worker 里也可以跑 canvas ,transferControlToOffscreen 到 worker 之后就可以直接画了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/858723

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX