【问题标题】:Best way to optimise canvas for high complexity drawings with React?使用 React 为高复杂度绘图优化画布的最佳方法?
【发布时间】:2020-08-07 11:18:49
【问题描述】:

我和一个朋友正在玩分形,并想创建一个交互式网站,您可以在其中更改生成分形的值,并且您可以看到它对生活的影响。在小分辨率测试中,网站响应速度很快,但仍然很慢。

drawFractal = () => {
  for (let x = 0; x < this.canvas.width; x++) {
    for (let y = 0; y < this.canvas.height; y++) {
      const belongsToSet = this.checkIfBelongsToMandelbrotSet(x / this.state.magnificationFactor - this.state.panX, y / this.state.magnificationFactor - this.state.panY);
      if (belongsToSet === 0) {
        this.ctx.clearRect(x,y, 1,1);
      } else {
        this.ctx.fillStyle = `hsl(80, 100%, ${belongsToSet}%)`;
        // Draw a colorful pixel
        this.ctx.fillRect(x,y, 1,1);
        }
      }
    }
}

checkIfBelongsToMandelbrotSet = (x,y) => {
  let realComponentOfResult = x;
  let imaginaryComponentOfResult = y;
  // Set max number of iterations
  for (let i = 0; i < this.state.maxIterations; i++) {
    const tempRealComponent = realComponentOfResult * realComponentOfResult - imaginaryComponentOfResult * imaginaryComponentOfResult + x;
    const tempImaginaryComponent = this.state.imaginaryConstant * realComponentOfResult * imaginaryComponentOfResult + y;
    realComponentOfResult = tempRealComponent;
    imaginaryComponentOfResult = tempImaginaryComponent;
    // Return a number as a percentage
    if (realComponentOfResult * imaginaryComponentOfResult > 5) {
      return (i / this.state.maxIterations * 100);
    }
  }
  // Return zero if in set
  return 0;
}

这是处理分形生成的算法。然而,我们遍历画布的每个像素,这是非常低效的。结果整个网站真的很慢。我想问一下使用 html canvas 是个好主意还是有更有效的替代方案?或者我可以优化 drawFractal() 函数以提高效率吗?我不知道如何从这一点继续,因为我没有经验,希望得到任何反馈!

【问题讨论】:

  • 您好@Middle,令人印象深刻的项目,但您的问题似乎更像是您项目的广告和推荐页面,而不是问题。请查看how to ask a good question 页面并重新编辑您的问题。如果您没有实际问题,请删除此帖子。
  • @MrPizzaGuy 谢谢,我现在改了。它更像是一个个人项目,因为它永远不会是一个网站。谢谢你

标签: javascript html reactjs html5-canvas fractals


【解决方案1】:

尽可能避免绘画操作。

当您执行fillRect(x, y, 1, 1) 时,浏览器必须每个像素从 CPU 转到 GPU 一次,这是非常低效的。

在您的情况下,由于您自己绘制每个像素,您可以简单地将所有这些像素设置在 ImageBitmap 上,然后每帧放置一次完整图像。

为了稍微改善颜色设置,我事先生成了一个包含一百个值的数组,如果您愿意,可以使其更精细。

您的 Mandelbrot 可能需要改进,我没有检查它,但这会比 StackOverflow 更适合 CodeReview

这是一个使用 800x600px 画布的简单演示:

const state = {
  magnificationFactor: 5000,
  imaginaryConstant: 1,
  maxIterations: 20,
  panX: 1,
  panY: 1
};

const canvas = document.getElementById('canvas');
const width = canvas.width = 800;
const height = canvas.height = 600;
const ctx = canvas.getContext('2d');
// the ImageData on which we will draw
const img = new ImageData( width, height );
// create an Uint32 view so that we can set one pixel in one op
const img_data = new Uint32Array( img.data.buffer );

const drawFractal = () => {
  for (let x = 0; x < width; x++) {
    for (let y = 0; y < height; y++) {
      const belongsToSet = checkIfBelongsToMandelbrotSet(x / state.magnificationFactor - state.panX, y / state.magnificationFactor - state.panY);
      // setthe value in our ImageData's data
      img_data[ y * width + x] = getColor( belongsToSet );
      }
    }
  // only now we paint
  ctx.putImageData( img, 0, 0 );
};

checkIfBelongsToMandelbrotSet = (x,y) => {
  let realComponentOfResult = x;
  let imaginaryComponentOfResult = y;
  // Set max number of iterations
  for (let i = 0; i < state.maxIterations; i++) {
    const tempRealComponent = realComponentOfResult * realComponentOfResult - imaginaryComponentOfResult * imaginaryComponentOfResult + x;
    const tempImaginaryComponent = state.imaginaryConstant * realComponentOfResult * imaginaryComponentOfResult + y;
    realComponentOfResult = tempRealComponent;
    imaginaryComponentOfResult = tempImaginaryComponent;
    // Return a number as a percentage
    if (realComponentOfResult * imaginaryComponentOfResult > 5) {
      return (i / state.maxIterations * 100);
    }
  }
  // Return zero if in set
  return 0;
}
// we generate all the colors at init instead of generating every frame
const colors = Array.from( { length: 100 }, (_,i) => {
  if( !i ) { return 0; }
  return hslToRgb( 80/360, 100/100, i/100 );
} );

function getColor( ratio ) {
  if( ratio === 0 ) { return 0; }
  return colors[ Math.round( ratio ) ];
}

function anim() {
  state.magnificationFactor -= 10;
  drawFractal();
  requestAnimationFrame( anim );
}
requestAnimationFrame( anim );


// original by mjijackson.com
// borrowed from https://stackoverflow.com/a/9493060/3702797
function hslToRgb(h, s, l){
    var r, g, b;

    if(s == 0){
        r = g = b = l; // achromatic
    }else{
        var hue2rgb = function hue2rgb(p, q, t){
            if(t < 0) t += 1;
            if(t > 1) t -= 1;
            if(t < 1/6) return p + (q - p) * 6 * t;
            if(t < 1/2) return q;
            if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
            return p;
        }

        var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        var p = 2 * l - q;
        r = hue2rgb(p, q, h + 1/3);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1/3);
    }
    // we want 0xAABBGGRR format
    function toHex( val ) {
      return  Math.round( val * 255 ).toString(16);
    }
    return Number( '0xFF' + toHex(b) + toHex(g) + toHex(r) );
}
&lt;canvas id="canvas"&gt;&lt;/canvas&gt;

【讨论】:

  • 漂亮!这显着提高了浏览器的性能
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-30
  • 1970-01-01
相关资源
最近更新 更多