我正在尝试使用canvas
(指代码中元素的2DContext)创建一个基本的绘画应用程序。但是,在当前时间,所有浏览器都放弃使用Maximum call stack size exceeded
。如何改进此代码,以便能够填充更大的区域?
我以fillAround(Math.round(x), Math.round(y), colorAt(Math.round(x), Math.round(y)), fillcolor);
开头的代码,其中x和y是点击的坐标。
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
function colorToRgb(arr) {
return {
r: arr[0],
g: arr[1],
b: arr[2]
}
}
function colorAt(xp, yp) {
return colorToRgb(canvas.getImageData(xp, yp, 1, 1).data);
}
function setColorAt(xp, yp, fill) {
var color = canvas.getImageData(xp, yp, 1, 1)
var set = hexToRgb(fill);
color.data[0] = set.r;
color.data[1] = set.g;
color.data[2] = set.b;
canvas.putImageData(color, xp, yp);
}
function sameColor(a, b) {
return a.r == b.r && a.g == b.r && a.b == b.b;
}
function fillAround(xp, yp, original, fill) {
if (sameColor(colorAt(xp, yp), original)) {
setColorAt(xp, yp, fill);
if (sameColor(colorAt(xp + 1, yp), original)) {
fillAround(xp + 1, yp, original, fill);
}
if (sameColor(colorAt(xp - 1, yp), original)) {
fillAround(xp - 1, yp, original, fill);
}
if (sameColor(colorAt(xp, yp + 1), original)) {
fillAround(xp, yp + 1, original, fill);
}
if (sameColor(colorAt(xp, yp - 1), original)) {
fillAround(xp, yp - 1, original, fill);
}
}
}
十六进制到RGB转换器来自RGB to Hex and Hex to RGB。
更新的代码(在@trincot的帮助下)
var canvasData;
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
function colorToRgb(arr) {
return {
r: arr[0],
g: arr[1],
b: arr[2]
}
}
function colorAt(xp, yp) {
return colorToRgb(canvasData.data.slice(4 * canvasTag.width * (yp - 1) + 4 * (xp + 1), 4 * canvasTag.widthwidth * (yp - 1) + 4 * xp + 8));
}
function setColorAt(xp, yp, fill) {
var set = hexToRgb(fill);
var o = 4 * canvasTag.width * (yp - 1) + 4 * (xp + 1);
canvasData.data[o] = set.r;
canvasData.data[o + 1] = set.g;
canvasData.data[o + 2] = set.b;
}
function sameColor(a, b) {
return a.r == b.r && a.g == b.r && a.b == b.b;
}
function fillAround(xp, yp, original, fill) {
const stack = [[xp, yp]];
while (stack.length) {
const [xp, yp] = stack.pop();
if (!sameColor(colorAt(xp, yp), original)) continue;
setColorAt(xp, yp, fill);
stack.push([xp + 1, yp], [xp - 1, yp], [xp, yp + 1], [xp, yp - 1]);
}
}
并通过
调用canvasData = canvas.getImageData(0, 0, canvasTag.width, canvasTag.height);
fillAround(Math.round(x), Math.round(y), colorAt(Math.round(x), Math.round(y)), fillcolor);
canvas.putImageData(canvasData, 0, 0);
答案 0 :(得分:1)
实际上,堆栈内存是有限的。您的代码会陷入fillAround
的嵌套非常深的调用中,这些调用会完全消耗可用的堆栈内存。
在不更改其他逻辑的情况下,建议将循环调用替换为管理自己的堆栈的循环:
function fillAround(xp, yp, original, fill) {
const stack = [[xp, yp]]; // Initially the stack has one pair of coordinates
while (stack.length) { // Keep iterating while there is work to do...
const [xp, yp] = stack.pop(); // Get one pair of coordinates from the stack
if (!sameColor(colorAt(xp, yp), original)) continue; // Skip it
setColorAt(xp, yp, fill);
// Push the neighbors onto the stack for later processing:
stack.push([xp + 1, yp], [xp - 1, yp], [xp, yp + 1], [xp, yp - 1]);
}
}
仅此一项并不能提高速度,只能避免堆栈内存异常。
为获得更好的性能,请勿使用以下命令分别读取/写入每个像素:
canvas.getImageData(xp, yp, 1, 1)
canvas.putImageData(color, xp, yp)
...,但要使用getImageData
的最后两个参数的作用:立即将整个画布区域读入内存,在内存中进行更改,然后仅用一个将其写回调用setImageData
。