Стирание ранее нарисованных линий на холсте HTML5
Чтобы поиграть с холстом HTML5, я решил сделать приложение, которое рисует аналоговую синхронизацию. Все отлично, за исключением того, что старые строки не стираются так, как я ожидал. Я включил часть кода ниже - DrawHands() вызывается один раз в секунду:
var hoursPoint = new Object();
var minutesPoint = new Object();
var secondsPoint = new Object();
function drawHands()
{
var now = new Date();
drawLine(centerX, centerY, secondsPoint.X, secondsPoint.Y, "white", 1);
var seconds = now.getSeconds();
secondsPoint = getOtherEndOfLine(centerX, centerY, 2 * Math.PI / 60 * seconds, 0.75 * radius);
drawLine(centerX, centerY, secondsPoint.X, secondsPoint.Y, "black", 1);
drawLine(centerX, centerY, minutesPoint.X, minutesPoint.Y, "white", 3);
var minutes = now.getMinutes();
minutesPoint = getOtherEndOfLine(centerX, centerY, 2 * Math.PI / 60 * minutes, 0.75 * radius);
drawLine(centerX, centerY, minutesPoint.X, minutesPoint.Y, "black", 3);
drawLine(centerX, centerY, hoursPoint.X, hoursPoint.Y, "white", 3);
var hours = now.getHours();
if (hours >= 12) { hours -= 12; } // Hours are 0-11
hoursPoint = getOtherEndOfLine(centerX, centerY, (2 * Math.PI / 12 * hours) + (2 * Math.PI / 12 / 60 * minutes), 0.6 * radius);
drawLine(centerX, centerY, hoursPoint.X, hoursPoint.Y, "black", 3);
}
Чтобы понять это, есть две вспомогательные функции:
- drawLine (x1, y1, x2, y2, цвет, толщина)
- getOtherEndOfLine (x, y, угол, длина)
Проблема в том, что, хотя все руки нарисованы, как ожидалось, в черном, они никогда не стираются. Я ожидал бы, что, поскольку одна и та же линия рисуется белым цветом (фоновым цветом), она эффективно стирает то, что было ранее нарисовано в этой точке. Но, похоже, это не так.
Что-нибудь мне не хватает?
Ответы
Ответ 1
По причинам, которые я мог бы расширить, вы должны рассмотреть возможность очистки вашего холста и его перерисовки полностью, если нет причин для производительности или компоновки.
Вы хотите clearRect, что-то вроде этого:
//clear the canvas so we can draw a fresh clock
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
//redraw your clock here
/* ... */
Ответ 2
Вместо того, чтобы стирать ненужные вещи, вы можете:
- сохранить состояние холста
- рисовать вещи, которые вы не хотите
- восстановить холст в сохраненное состояние, чтобы "стереть" их
Это можно сделать довольно легко с помощью ImageData
:
var canvas = document.querySelector('canvas'),
context = canvas.getContext('2d');
context.fillStyle = 'blue';
context.fillRect(0,0,200,200);
// save the state of the canvas here
var imageData = context.getImageData(0,0,canvas.width,canvas.height);
// draw a red rectangle that we'll get rid of in a second
context.fillStyle = 'red';
context.fillRect(50,50,100,100);
setTimeout(function () {
// return the canvas to the state right after we drew the blue rect
context.putImageData(imageData, 0, 0);
}, 1000);
<canvas width=200 height=200>
Ответ 3
Причина, по которой вы не можете просто перерисовать линию в белом цвете и надеяться на ее стирание старой строки, состоит в том, что может быть некоторое сглаживание/кровотечение. Вы также заметите, что прямая горизонтальная линия, нарисованная на пикселе в сравнении с полупикселем, выглядит очень по-другому из-за этого.
Когда вы делаете белые линии "стирания", попробуйте рисовать их с большим lineWidth
примерно на 3 или 4. Это должно работать для вашего дела.
Вы также должны нарисовать все белые линии сначала, а затем все черные линии, если они пересекаются.
Ответ 4
Быстрый и простой способ очистки холста - установить ширину:
context.canvas.width = context.canvas.width;
Ответ 5
Мое решение - двойная буферизация:
var shapes =
[{type:"circle", x:50, y:50, radious:40, lineWidth:2, strokeStyle:"#FF0000", fillStyle:"#800000"}
,{type:"rectangle", x:50, y:50, width:100, height: 100, lineWidth:2, strokeStyle:"#00FF00", fillStyle:"#008000"}
,{type:"line", x1:75, y1:100, x2:170, y2:75, lineWidth:3, strokeStyle:"#0000FF"}
];
step1();
setTimeout(function () {
step2();
setTimeout(function () {
step3();
}, 1000);
}, 1000);
function step1() {
clearCanvas('myCanvas1');
shapes.forEach((sh) => { drawShape('myCanvas1', sh); });
};
function step2() {
clearCanvas('myCanvas2');
shapes.pop();
shapes.forEach((sh) => { drawShape('myCanvas2', sh); });
showOtherCanvas('myCanvas2', 'myCanvas1');
};
function step3() {
clearCanvas('myCanvas1');
shapes.pop();
shapes.forEach((sh) => { drawShape('myCanvas1', sh); });
showOtherCanvas('myCanvas1', 'myCanvas2');
};
function showOtherCanvas(cnv1, cnv2) {
var c1 = document.getElementById(cnv1);
var c2 = document.getElementById(cnv2);
c1.style['z-index'] = 3;
c2.style['z-index'] = 1;
c1.style['z-index'] = 2;
}
function clearCanvas(canvasID) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.fillStyle="#FFFFFF";
ctx.fillRect(0,0,480,320);
}
function drawShape (canvasID, info) {
switch (info.type) {
case "line" : drawLine(canvasID, info);
case "rectangle" : drawRectangle(canvasID, info);
case "circle" : drawCircle(canvasID, info);
}
}
function drawLine (canvasID, info) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.strokeStyle = info.strokeStyle;
ctx.lineWidth = info.lineWidth
ctx.beginPath();
ctx.moveTo(info.x1, info.y1);
ctx.lineTo(info.x2, info.y2);
ctx.stroke();
}
function drawRectangle (canvasID, info) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.fillStyle = info.fillStyle;
ctx.strokeStyle = info.strokeStyle;
ctx.lineWidth = info.lineWidth
ctx.fillRect(info.x, info.y, info.width, info.height);
ctx.strokeRect(info.x, info.y, info.width, info.height);
}
function drawCircle (canvasID, info) {
var canvas = document.getElementById(canvasID);
var ctx = canvas.getContext('2d');
ctx.fillStyle = info.fillStyle;
ctx.strokeStyle = info.strokeStyle;
ctx.lineWidth = info.lineWidth
ctx.beginPath();
ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(info.x, info.y, info.radious, 0, 2 * Math.PI);
ctx.stroke();
}
<canvas id="myCanvas2" width="480" height="320"
style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:1">
</canvas>
<canvas id="myCanvas1" width="480" height="320"
style="border: 1px solid #000000; position: absolute; top: 10; left: 10; z-index:2">
</canvas>