Я пытаюсь сделать программу для рисования с помощью HTML canvas. Вы можете увидеть его версию здесь: https://naclcaleb.github.io/Draw
Моя архитектура работает примерно так:
У меня есть главный объект Canvas, который содержит список слоев, которые он рисует.
Каждый слой имеет список «Штрихов». Каждый штрих содержит список действий, необходимых для его рисования.
У меня было несколько проблем с задержкой. Во-первых, рисование всех штрихов в списке заняло слишком много времени.
Я решил это путем «рендеринга» слоя при каждом добавлении обводки, так что это просто изображение.
Это работало нормально, за исключением того, что мне все равно приходилось рисовать каждый штрих, когда пользователь нажимал Ctrl + Z, чтобы я мог сбросить рендер.
Так что каждый раз, когда они пытались отменить, вычисление занимало некоторое время - пригодное для использования, но не идеальное.
Чтобы это исправить, я пробовал один и тот же подход с каждым штрихом - когда он будет создан, «визуализируем» его, рисуя на другом скрытом холсте, затем превращая его в изображение с помощью DataURL.
(ПРИМЕЧАНИЕ: я попытался использовать getImageData
и putImageData
вместо этого, но тогда он заменит предыдущие штрихи пустыми пикселями)
А теперь у меня довольно большая проблема:
Всякий раз, когда я делаю удар, он немедленно исчезает. Но как только я нажимаю Ctrl + Z, все предыдущие штрихи внезапно становятся видимыми.
Я пытался понять это некоторое время.
Вот в основном то, что происходит:
Мышь освобождается, что вызывает прослушиватель этого события (находится в классе слоя):
this.el.addEventListener("mouseup", function(e){
//You can ignore this line...
if (active){
//...and this if statement. It just handles the erasers, and has been tested to work
if (that.erasing){
CURRENT_TOOL.currentStroke.actions.push({
func: ctx.setGlobalCompositeOperation,
params: ["source-over"]
});
}
//Have the current stroke create its render - I have used console.log to ensure that this does get run, and that the dataURL produced shows the correct image.
CURRENT_TOOL.currentStroke.createRender();
//The endStroke method returns the currentStroke, which I grab here
var newStroke = CURRENT_TOOL.endStroke(ctx);
//"that" is just a reference to the layer instance
//Add it to the strokes list
that.strokes.push(newStroke);
//Update the render
that.updateRender();
}
});
Как вы видели в приемнике событий, он вызывает метод updateRender
. Вот этот метод (также в классе слоя):
//Clear the canvas
this.ctx.clearRect(0, 0, this.el.width, this.el.height);
//Put the LAYER's render onto the screen
this.ctx.putImageData(this.render, 0, 0);
//Draw the stroke - I'll give you the draw function, but I've confirmed it does get run
this.strokes[this.strokes.length-1].draw();
//Now get the image data and update the render
this.render = this.ctx.getImageData(0,0, this.el.width, this.el.height);
Как вы видели в этой функции, она вызывает функцию рисования обводки (находится в классе обводки):
draw(){
//Ignore this if
if (this.render === undefined){
for (var i = 0;i<this.actions.length;i++){
this.actions[i].func.apply(this.ctx, this.actions[i].params);
}
}
else {
//I've used console.log to confirm that this runs.
//Draw the render
this.ctx.drawImage(this.render, 0, 0);
//I've also console.logged the dataurl of the layer before and after drawing the image, and neither have the render drawn on them.
}
}
Кажется, это говорит о том, что this.ctx.drawImage
не запускается? Или что он работает неправильно?
В любом случае, это так, и хотя я извиняюсь за долговечность этого вопроса, я надеюсь, что кто-то может мне помочь. Это действительно мое последнее средство.
Еще одна вещь: я заставил его работать по ссылке, которую я дал ранее, рендеринг слоя, используя тот же метод, что и рендеринг штрихов - используя изображение с dataURL вместо getImageData
и putImageData
. Я не совсем уверен, что думать об этом ...