【问题标题】:Improving Canvas draw call Performance提高 Canvas 绘制调用性能
【发布时间】:2021-07-24 20:20:47
【问题描述】:

我想画很多点。我就是这样做的:

let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

function render () {
    window.requestAnimationFrame(render);
    // clear screen
    ctx.clearRect(0, 0, cwidth, cheight);
    for (let p of particles) {
        p.rmove();
        p.render(ctx);
    }
}

render();

我点的绘图函数如下所示:

class Point {

    x: number;
    y: number;

    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    rmove() {
        this.x += Math.round(Math.random() < 0.5 ? -1 : 1);
        this.y += Math.round(Math.random() < 0.5 ? -1 : 1);
    }

    render (ctx) {
        ctx.fillStyle = "gray";
        ctx.beginPath();
        ctx.rect(this.x, this.y, 1.5,1.5);
        ctx.fill();
        ctx.stroke();
    }
}

请注意,我将 rmove() 函数中的值四舍五入,因为画布使用整数坐标绘制点的速度更快。

我想以某种方式将所有这些绘图调用放在一起。

【问题讨论】:

标签: javascript typescript canvas


【解决方案1】:

在给定的上下文(甚至可能是 Path2D)上提出您的观点 trace,并为渲染器保留实际绘图。

您所要做的就是在跟踪rect 之前使上下文moveTo 具有自己的坐标。

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    rmove() {
        this.x += Math.round(Math.random() < 0.5 ? -1 : 1);
        this.y += Math.round(Math.random() < 0.5 ? -1 : 1);
    }

    trace (ctx) {
      ctx.moveTo( this.x, this.y );
      ctx.rect(this.x, this.y, 1.5, 1.5);
    }
}

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const cwidth = canvas.width = 300;
const cheight = canvas.height = 300;

const particles = Array.from(
  { length: 5000 },
  ()=> new Point( cwidth/2, cheight/2 )
);


function animate () {
  update();
  draw();
  window.requestAnimationFrame(animate);
}
function update() {
  for (let p of particles) {
    p.rmove();
  }
}
function draw() {
  // clear screen
  ctx.clearRect(0, 0, cwidth, cheight);

  // define our single path
  ctx.beginPath();
  for (let p of particles) {
    p.trace(ctx);
  }
  ctx.fillStyle = "gray";
  ctx.stroke(); // OP has it reversed, but then the fill-color is almost not visible
                // (1.5 width - 2*0.5 stroke leaves only 0.5 for the fill => antialiased...
  ctx.fill();
}

window.requestAnimationFrame( animate );
&lt;canvas id="canvas"&gt;&lt;/canvas&gt;

但这只是因为你所有的粒子都共享相同的颜色。如果他们没有,那么你需要更多的逻辑:

const colors = ['red', 'green', 'blue', 'cyan', 'magenta', 'yellow'];
class Point {
    constructor(x, y, color=0) {
        this.x = x;
        this.y = y;
        this.color = color;
    }

    rmove() {
        this.x += Math.round(Math.random() < 0.5 ? -1 : 1);
        this.y += Math.round(Math.random() < 0.5 ? -1 : 1);
    }

    trace (ctx) {
      ctx.moveTo( this.x, this.y );
      ctx.rect(this.x, this.y, 1.5, 1.5);
    }
}

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const cwidth = canvas.width = 300;
const cheight = canvas.height = 300;

const particles = Array.from(
  { length: 5000 },
  ()=> new Point( cwidth/2, cheight/2, (Math.random()*colors.length-1)|0 )
);


function animate () {
  update();
  draw();
  window.requestAnimationFrame(animate);
}
function update() {
  for (let p of particles) {
    p.rmove();
  }
}
function draw() {
  // clear screen
  ctx.clearRect(0, 0, cwidth, cheight);

  // define our single path
  let last_color = -1;
  for (let p of particles) {
    let p_color = p.color;
    if( p_color !== last_color ) {
      paint();
      last_color = p_color;
    }
    p.trace(ctx);
  }
  paint(); // the last
  
  function paint() {
    ctx.fillStyle = colors[ last_color ];
    ctx.strokeStyle = colors[ (last_color + 1) % colors .length ];
    ctx.stroke();
    ctx.fill();
    ctx.beginPath();
  }
}

window.requestAnimationFrame( animate );
&lt;canvas id="canvas"&gt;&lt;/canvas&gt;

尽管这样做,您最终可能会得到很多图纸,因此最后一个可能不适用于所有地方的技巧是按粒子颜色对粒子进行排序。这会产生不同的图形,因为这一种颜色将始终位于顶部,但在某些情况下它可能会起作用,并且性能提升可能会带来不利影响。

const colors = ['red', 'green', 'blue', 'cyan', 'magenta', 'yellow'];
class Point {
    constructor(x, y, color=0) {
        this.x = x;
        this.y = y;
        this.color = color;
    }

    rmove() {
        this.x += Math.round(Math.random() < 0.5 ? -1 : 1);
        this.y += Math.round(Math.random() < 0.5 ? -1 : 1);
    }

    trace (ctx) {
      ctx.moveTo( this.x, this.y );
      ctx.rect(this.x, this.y, 1.5, 1.5);
    }
}

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const cwidth = canvas.width = 300;
const cheight = canvas.height = 300;

const particles = Array.from(
  { length: 5000 },
  ()=> new Point( cwidth/2, cheight/2, (Math.random()*colors.length-1)|0 )
);

particles.sort( (a, b) => a.color - b.color );

function animate () {
  update();
  draw();
  window.requestAnimationFrame(animate);
}
function update() {
  for (let p of particles) {
    p.rmove();
  }
}
function draw() {
  // clear screen
  ctx.clearRect(0, 0, cwidth, cheight);

  // define our single path
  let last_color = -1;
  for (let p of particles) {
    let p_color = p.color;
    if( p_color !== last_color ) {
      paint();
      last_color = p_color;
    }
    p.trace(ctx);
  }
  paint(); // the last
  
  function paint() {
    ctx.fillStyle = colors[ last_color ];
    ctx.strokeStyle = colors[ (last_color + 1) % colors .length ];
    ctx.stroke();
    ctx.fill();
    ctx.beginPath();
  }
}

window.requestAnimationFrame( animate );
&lt;canvas id="canvas"&gt;&lt;/canvas&gt;

没有什么能阻止你从这些排序的粒子生成块,所以它看起来更随机。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-17
    • 2011-05-14
    • 2015-03-12
    • 1970-01-01
    • 1970-01-01
    • 2013-01-29
    • 1970-01-01
    相关资源
    最近更新 更多