选中此jsfiddle demo,
唯一的区别是单击之前在左侧画布中绘制了笔触矩形,单击时在右侧画布中绘制了矩形。
function getPointColor(ctx, x, y) {
return ctx.getImageData(x, y, 1, 1).data;
}
function getTime(ctx, action) {
console.time(action);
for (let i = 0; i < 1000; i++) {
getPointColor(ctx, 1, 1);
}
console.timeEnd(action);
}
ctx1.strokeRect(0, 0, 20, 20)
canvas1.onclick = function () {
getTime(ctx1, 'click canvas1');
}
canvas2.onclick = function () {
ctx2.strokeRect(0, 0, 20, 20)
getTime(ctx2, 'click canvas2');
}
并且控制台显示出canvas1的耗时是canvas2的100倍;
最佳答案
这是a Chrome bug,大约是v76开始的。
这似乎与“硬件加速”相关联,禁用此选项将使两个操作花费相同的最短时间。
请注意,尽管当前的Canary版本(v78)现在具有不同的行为(可能是由于this commit所致),其中两种操作都将花费最大的时间...
我已经打开了链接的错误报告,因此除了您可以在此处提出修复建议之外,没有更多的事情要做。
如果我不得不猜测会发生什么,我会说他们现在大部分时间将画布缓冲区保存在GPU内存中,但是要获取ImageData,他们需要将其传输回CPU。因此,像您一样多次调用此方法将需要他们多次执行此传输。
因此,无论您是否解决该错误,都应该使用一个简单的修补程序,以仅执行一次getPointColor
的方式重写您的getImageData
。
不必多次调用getImageData(x, y, 1, 1)
来获取不同的点,您应该只获取一个代表所有画布的ImageData,然后从这个大的ImageData中选择像素的值:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// gets the slot at x y coords from a flatten pixel matrix represented as 32bits
function getPixelColor32(data, x, y, width) {
return data[y * width + x];
}
// set the value of the slots at x, y (32bits version)
function setPixelColor32(val, data, x, y, width) {
data[y * width + x] = val;
}
// the same functions
// in case you wish to keep the orignal Uint8ClampedArray from ImageData
// returns a new Uint8ClampedArray, not that good for memory
function getPixelColor8(data, x, y, width) {
const index = (y * width + x) * 4;
return data.slice(index, index + 4);
}
// here vals is an Array like object of length 4 [r, g, b, a]
function setPixelColor8(vals, data, x, y, width) {
const index = (y * width + x) * 4;
for(let i = 0; i < 4; i ++) {
data[index + i] = vals[i];
}
}
// so we can draw a circle
function distanceFromCenter(x, y, cx, cy) {
return Math.hypot(cx - x, cy - y);
}
let mouse_down = false;
canvas.onmousedown = (evt) => {
mouse_down = true;
draw(evt);
};
canvas.onmousemove = (evt) => {
if(mouse_down) {
draw(evt);
}
};
canvas.onmouseup = (evt) => {
mouse_down = false;
}
function draw(evt) {
const { width, height } = canvas;
const rad = height / 10;
const cx = evt.offsetX;
const cy = evt.offsetY;
// we call getImageData only once
const imgData = ctx.getImageData(0, 0, width, height);
// using an Uint32Array view for easier pixel manipulation
// we could obviously also use the Uint8Clamped version,
// though it would use more memory
const data = new Uint32Array(imgData.data.buffer);
for( let x = 0; x < width; x ++) {
for( let y = 0; y < height; y ++) {
// make a circle
if(distanceFromCenter(x, y, cx, cy) < rad + 1) {
// get the pixel value (here as 32bit)
const px = getPixelColor32(data, x, y, width);
// do something with it (negative)
setPixelColor32(~px | 0xFF000000, data, x, y, width);
}
}
}
ctx.putImageData(imgData, 0, 0);
};
// just to draw something at beginning
const px_size = 10;
const noise = new ImageData(canvas.width / px_size, canvas.height / px_size);
new Uint32Array(noise.data.buffer)
.forEach((v, i, a) => {
a[i] = Math.random() * 0xFFFFFF + 0xFF000000;
});
ctx.putImageData(noise, 0 ,0);
ctx.scale(px_size, px_size);
ctx.imageSmoothingEnabled = false;
ctx.drawImage(canvas, 0, 0);
ctx.setTransform(1,0,0,1,0,0);
ctx.imageSmoothingEnabled = true;
<pre>click & drag to do pixel manip</pre>
<canvas id="canvas"></canvas>
关于javascript - 为什么在不同情况下执行canvas函数getImageData时会有很大的耗时,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57834004/