gpt4 book ai didi

javascript - 预渲染与实时 Canvas 渲染时的奇怪结果

转载 作者:行者123 更新时间:2023-12-03 01:07:28 24 4
gpt4 key购买 nike

我有一个方法,可以在使用 requestAnimationFrame 下次重新绘制之前在 Canvas 中渲染矩形矩阵。我正在努力实现最佳表现。我解决这个问题的第一个方法是在 Canvas 中实时创建矩形。

  render(display: PanelDisplay): void {
const ctx = this.parameters.canva.getContext("2d");
const widthEachBit = Math.floor(this.parameters.canva.width / display[0].length);
const heightEachBit = Math.floor(this.parameters.canva.height / display.length);

ctx.lineWidth = 1;
ctx.strokeStyle = this.parameters.colorStroke;

for(var i = 0; i < display.length; i++) {
for(var j = 0; j < display[i].length; j++) {
const x = j*widthEachBit;
const y = i*heightEachBit;
ctx.beginPath();
ctx.fillStyle = display[i][j] == 1 ? this.parameters.colorBitOn : this.parameters.colorBitOff;
ctx.rect(x, y, widthEachBit, heightEachBit);
ctx.fill();
ctx.stroke();
}
}
}

这样做会导致 3k 元素矩阵的性能平庸:

  • Chrome:20-30 fps
  • 火狐:40 fps

作为第二种方法,我决定预渲染两个矩形并使用 drawImage 将它们渲染在 Canvas 上:

  render(display: PanelDisplay): void {
const ctx = this.parameters.canva.getContext("2d");
const widthEachBit = Math.floor(this.parameters.canva.width / display[0].length);
const heightEachBit = Math.floor(this.parameters.canva.height / display.length);

// Render the different canvas once before instead of recalculating every loop
const prerenderedBitOn = this._prerenderBit(this._prerenderedOn, widthEachBit, heightEachBit, this.parameters.colorBitOn);
const prerenderedBitOff = this._prerenderBit(this._prerenderedOff, widthEachBit, heightEachBit, this.parameters.colorBitOff);

for(var i = 0; i < display.length; i++) {
for(var j = 0; j < display[i].length; j++) {
const x = j*widthEachBit;
const y = i*heightEachBit;
ctx.drawImage(display[i][j] == 1 ? prerenderedBitOn : prerenderedBitOff, x, y);
}
}
}

private _prerenderBit(canvas: HTMLCanvasElement, widthEachBit: number, heightEachBit: number, color: string) {
canvas.width = widthEachBit;
canvas.height = heightEachBit;
const ctx = canvas.getContext('2d');

ctx.beginPath();
ctx.fillStyle = color;
ctx.rect(0, 0, widthEachBit, heightEachBit);
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = this.parameters.colorStroke;
ctx.stroke();

return canvas;
}

这样做的结果是,我在 Firefox 中得到了更好的结果,而在 Chrome 中得到了最差的结果:

  • Chrome:10 fps
  • 火狐:50 fps

我不太确定如何解释这些结果。作为第三种方法,我正在考虑预先创建 n 个 Canvas,其中 n 是矩阵的大小,并且仅更新下一次重新绘制之前需要的内容。在此之前,我想了解一下为什么我在一个浏览器上预渲染的结果更好,而在另一种浏览器上预渲染的结果最差。我还希望得到任何反馈以获得更好的性能。如有必要,我可以提供性能堆栈跟踪。

最佳答案

不同的结果可能是由于 API 的不同实现,甚至可能是您在浏览器上设置的不同设置导致的,这使得一个人更喜欢 GPU 加速而不是 CPU 计算,或者一个人比另一个人处理更好的图形内存或者其他什么。

但无论如何,如果我正确理解您的代码,您可以获得比这两个选项更好的结果。

我可以想到两种主要方法,您必须测试它们。

第一个是以一种颜色渲染整个矩阵大小的一个大矩形,然后循环使用另一种颜色的所有单元格,并将它们组合在一个子路径中,以便您调用 fill( ) 仅在此子路径组合的末尾出现一次。

最后,您将在所有这些上绘制网格(网格可以是简单的十字图案,或者在屏幕外 Canvas 上预渲染,或者再次是单个子路径)。

const W = 50;
const H = 50;
const cellSize = 10;
const grid_color = 'black';
var grid_mode = 'inline';


const ctx = canvas.getContext('2d');
const matrix = [];

canvas.width = W * cellSize;
canvas.height = H * cellSize;

for (let i = 0; i < H; i++) {
let row = [];
matrix.push(row);
for (let j = 0; j < W; j++) {
row.push(Math.random() > 0.5 ? 0 : 1);
}
}

const grid_pattern = generateGridPattern();
const grid_img = generateGridImage();

draw();

function draw() {
shuffle();

// first draw all our green rects ;)
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, canvas.width, canvas.height);

// now draw all the red ones
ctx.fillStyle = 'red';
ctx.beginPath(); // single sub-path declaration
for (let i = 0; i < H; i++) {
for (let j = 0; j < W; j++) {
// only if a red cell
if (matrix[i][j])
ctx.rect(i * cellSize, j * cellSize, cellSize, cellSize);
}
}
ctx.fill(); // single fill operation
drawGrid();

requestAnimationFrame(draw);

}

function shuffle() {
let r = Math.floor(Math.random() * H);
for (let i = r; i < r + Math.floor(Math.random() * (H - r)); i++) {
let r = Math.floor(Math.random() * W);
for (let j = r; j < r + Math.floor(Math.random() * (W - r)); j++) {
matrix[i][j] = +!matrix[i][j];
}
}
}

function drawGrid() {
if (grid_mode === 'pattern') {
ctx.fillStyle = grid_pattern;
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.translate(-cellSize / 2, -cellSize / 2);
ctx.fill();
ctx.setTransform(1, 0, 0, 1, 0, 0);
} else if (grid_mode === 'image') {
ctx.drawImage(grid_img, 0, 0);
} else {
ctx.strokeStyle = grid_color;
ctx.beginPath();
for (let i = 0; i <= cellSize * H; i += cellSize) {
ctx.moveTo(0, i);
ctx.lineTo(cellSize * W, i);
for (let j = 0; j <= cellSize * W; j += cellSize) {
ctx.moveTo(j, 0);
ctx.lineTo(j, cellSize * H);
}
}
ctx.stroke();
}
}

function generateGridPattern() {
const ctx = Object.assign(
document.createElement('canvas'), {
width: cellSize,
height: cellSize
}
).getContext('2d');
// make a cross
ctx.beginPath();
ctx.moveTo(cellSize / 2, 0);
ctx.lineTo(cellSize / 2, cellSize);
ctx.moveTo(0, cellSize / 2);
ctx.lineTo(cellSize, cellSize / 2);

ctx.strokeStyle = grid_color;
ctx.lineWidth = 2;
ctx.stroke();

return ctx.createPattern(ctx.canvas, 'repeat');
}

function generateGridImage() {
grid_mode = 'inline';
drawGrid();
const buf = canvas.cloneNode(true);
buf.getContext('2d').drawImage(canvas, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
return buf;
}
field.onchange = e => {
grid_mode = document.querySelector('input:checked').value;
}
<fieldset id="field">
<legend>Draw grid using:</legend>
<label><input name="grid" type="radio" value="inline" checked>inline</label>
<label><input name="grid" type="radio" value="pattern">pattern</label>
<label><input name="grid" type="radio" value="image">image</label>
</fieldset>

<canvas id="canvas"></canvas>

您可以采取的另一种完全不同的方法是直接操作 ImageData。将其设置为矩阵的大小(cellSize 为 1),将其放在 Canvas 上,最后按比例重新绘制它,然后绘制网格。

ctx.putImageData(smallImageData, 0,0);
ctx.imageSmoothingEnabled = false;
ctx.drawImage(ctx.canvas, 0, 0, ctx.canvas.width, ctx.canvas.height);
drawgrid();

const W = 50;
const H = 50;
const cellSize = 10;
const grid_color = 'black';

canvas.width = W * cellSize;
canvas.height = H * cellSize;

const ctx = canvas.getContext('2d');
// we'll do the matrix operations directly on an imageData
const imgData = ctx.createImageData(W, H);
const matrix = new Uint32Array(imgData.data.buffer);

const red = 0xFF0000FF;
const green = 0xFF008000;

for (let i = 0; i < H*W; i++) {
matrix[i] = (Math.random() > 0.5 ? green : red);
}

prepareGrid();
ctx.imageSmoothingEnabled = false;
draw();

function draw() {
shuffle();
// put our update ImageData
ctx.putImageData(imgData, 0, 0);
// scale its result
ctx.drawImage(ctx.canvas,
0,0,W,H,
0,0,canvas.width,canvas.height
);
// draw the grid which is already drawn in memory
ctx.stroke();
requestAnimationFrame(draw);
}
function shuffle() {
// here 'matrix' is actually the data of our ImageData
// beware it is a 1D array, so we need to normalize the coords
let r = Math.floor(Math.random() * H);
for (let i = r; i < r + Math.floor(Math.random() * (H - r)); i++) {
let r = Math.floor(Math.random() * W);
for (let j = r; j < r + Math.floor(Math.random() * (W - r)); j++) {
matrix[i*W + j] = matrix[i*W + j] === red ? green : red;
}
}
}
function prepareGrid() {
// we draw it only once in memory
// 'draw()' will then just have to call ctx.stroke()
ctx.strokeStyle = grid_color;
ctx.beginPath();
for (let i = 0; i <= cellSize * H; i += cellSize) {
ctx.moveTo(0, i);
ctx.lineTo(cellSize * W, i);
for (let j = 0; j <= cellSize * W; j += cellSize) {
ctx.moveTo(j, 0);
ctx.lineTo(j, cellSize * H);
}
}
}
<canvas id="canvas"></canvas>

关于javascript - 预渲染与实时 Canvas 渲染时的奇怪结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52335088/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com