Я хочу использовать инструмент линии стиля рисования MS, чтобы рисовать прямую линию с помощью пользовательского интерфейса на элементе холста html с использованием JS.
Проблема в том, что предварительный просмотр линии должен быть виден на холсте, поскольку мышь перетаскивается после щелчка в начальной позиции (предварительный просмотр линии представляет собой, по сути, прямую линию от начальной точки щелчка до текущего положения мыши). Как только мышь переместится в новое положение без отпускания, предварительный просмотр старой строки должен исчезнуть и появиться новая. Наконец, как только мышь будет отпущена, на холсте должна быть нарисована последняя линия. Вместо этого я получаю изображение, которое вы видите ниже.
Я пробовал различные способы выполнить следующие шаги в этом порядке, но безуспешно:
Повторяйте шаги 2 и 3, пока мышь не будет отпущена:
window.onload = function() {
const canvas = document.getElementById('paintCanvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let painting = false;
let startX, startY;
let canvasState = false;
canvas.addEventListener('mousedown', startPosition);
canvas.addEventListener('mouseup', endPosition);
canvas.addEventListener('mousemove', draw);
ctx.lineWidth = 5;
ctx.lineCap = 'round';
function startPosition(e) {
painting = true;
[startX, startY] = [e.offsetX, e.offsetY];
ctx.save();
ctx.beginPath();
draw(e);
}
function endPosition() {
ctx.closePath();
ctx.restore();
painting = false;
canvasState = false;
}
function draw(e) {
if (!painting) return;
if (selectedTool.id === 'pencil') {
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
} else if (selectedTool.id === 'line') {
// write code that works with a line
if (e.type === 'mousemove') {
// remove old preview line
if (canvasState) {
ctx.putImageData(canvasState, 0, 0);
} else {
canvasState = ctx.getImageData(0,0,canvas.width,canvas.height);
}
// paint straight line from original start to current mouse position
ctx.moveTo(startX, startY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
}
} else if (selectedTool.id === 'eraser') {
if (e.type === 'mousedown') {
ctx.strokeStyle = ctx.fillStyle;
ctx.lineWidth += 5;
} else {
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
}
}
}
};
Я думаю, это потому, что canvasState
восстановлена внутри здания дорожка. Следующее исправление решило проблему для меня:
// remove old preview line
if (canvasState) {
ctx.putImageData(canvasState, 0, 0);
} else {
canvasState = ctx.getImageData(0,0,canvas.width,canvas.height);
}
// paint straight line from original start to current mouse position
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.closePath();
ctx.stroke();
чтобы ограничить перерисовку строки, я добавил ---> let [endX, endY] = [e.offsetX, e.offsetY]; после ctx.stoke(), а затем досрочно вернуться из функции рисования, если расстояние между (endX,endY) и e.offsetX, e.offsetY меньше, чем lineWidth.
Почему бы не сделать так, чтобы линия предварительного просмотра обновлялась динамически при движении мыши, а окончательная линия рисовалась бы точно, когда мышь отпущена.
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "UTF-8">
<meta name = "viewport" content = "width=device-width, initial-scale=1.0">
<title>Canvas Line Tool</title>
<style>
#paintCanvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id = "paintCanvas" width = "800" height = "600"></canvas>
<script>
window.onload = function() {
const canvas = document.getElementById('paintCanvas');
const ctx = canvas.getContext('2d');
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let painting = false;
let startX, startY;
let canvasState = null;
canvas.addEventListener('mousedown', startPosition);
canvas.addEventListener('mouseup', endPosition);
canvas.addEventListener('mousemove', draw);
ctx.lineWidth = 5;
ctx.lineCap = 'round';
function startPosition(e) {
painting = true;
[startX, startY] = [e.offsetX, e.offsetY];
canvasState = ctx.getImageData(0, 0, canvas.width, canvas.height);
}
function endPosition() {
painting = false;
ctx.putImageData(canvasState, 0, 0);
drawFinalLine(); // Draw the final line
}
function draw(e) {
if (!painting) return;
ctx.putImageData(canvasState, 0, 0);
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
}
function drawFinalLine() {
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(event.offsetX, event.offsetY);
ctx.stroke();
}
};
</script>
</body>
</html>
это именно то, на что был нацелен мой код, но вызов BeginPath() был размещен неправильно.
...Как только мышь переместится в новое положение без отпускания, старая линия предварительный просмотр должен исчезнуть и должен появиться новый...
Не знаю, правильно ли я вас понял, но судя по изображению, ваша проблема в том, что новая строка не удаляет предыдущую и следовательно, как и на изображении, все линии у вас лежат на холсте.
Посмотрите на этот подход:
const canvas = document.getElementById('sketch');
const ctx = canvas.getContext('2d');
let isDrawing = false;
let startX, startY;
canvas.addEventListener('mousedown', (e) => {
startX = e.offsetX;
startY = e.offsetY;
isDrawing = true;
});
canvas.addEventListener('mousemove', (e) => {
if (isDrawing) {
const currentX = e.offsetX;
const currentY = e.offsetY;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(currentX, currentY);
ctx.stroke();
}
});
canvas.addEventListener('mouseup', (e) => {
if (isDrawing) {
const endX = e.offsetX;
const endY = e.offsetY;
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.stroke();
isDrawing = false;
}
});
canvas.addEventListener('mouseleave', () => {
if (isDrawing) {
isDrawing = false;
}
});
#sketch {
border: 2px solid red;
display: block;
margin: 20px auto;
}
<canvas id = "sketch" width = "800" height = "600"></canvas>
РЕДАКТИРОВАТЬ
Если вы хотите сохранить линии canvas
:
const canvas = document.getElementById('sketch');
const ctx = canvas.getContext('2d');
let isDrawing = false;
let startX, startY;
let lines = [];
canvas.addEventListener('mousedown', (e) => {
startX = e.offsetX;
startY = e.offsetY;
isDrawing = true;
});
canvas.addEventListener('mousemove', (e) => {
if (isDrawing) {
const currentX = e.offsetX;
const currentY = e.offsetY;
redrawCanvas();
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(currentX, currentY);
ctx.stroke();
}
});
canvas.addEventListener('mouseup', (e) => {
if (isDrawing) {
const endX = e.offsetX;
const endY = e.offsetY;
lines.push({startX, startY, endX, endY});
isDrawing = false;
}
});
canvas.addEventListener('mouseleave', () => {
if (isDrawing) {
isDrawing = false;
}
});
function redrawCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = 'black';
ctx.lineWidth = 2;
lines.forEach(line => {
ctx.beginPath();
ctx.moveTo(line.startX, line.startY);
ctx.lineTo(line.endX, line.endY);
ctx.stroke();
});
}
#sketch {
border: 2px solid red;
display: block;
margin: 20px auto;
}
<canvas id = "sketch" width = "800" height = "600">
Я не пробовал это решение, но, похоже, вы очищаете весь холст перед перерисовкой — это нежелательно, поскольку при этом не сохраняются другие штрихи на холсте.
@jam Я думал, ты именно это имел в виду в цитате из твоего вопроса, которую я привел в своем ответе. Посмотрите редактирование, если я хорошо понимаю ваши потребности.
спасибо, что выявили причину. Это устранило проблему.