HTML Canvas нарисовать прямую линию

Я хочу использовать инструмент линии стиля рисования MS, чтобы рисовать прямую линию с помощью пользовательского интерфейса на элементе холста html с использованием JS.

Проблема в том, что предварительный просмотр линии должен быть виден на холсте, поскольку мышь перетаскивается после щелчка в начальной позиции (предварительный просмотр линии представляет собой, по сути, прямую линию от начальной точки щелчка до текущего положения мыши). Как только мышь переместится в новое положение без отпускания, предварительный просмотр старой строки должен исчезнуть и появиться новая. Наконец, как только мышь будет отпущена, на холсте должна быть нарисована последняя линия. Вместо этого я получаю изображение, которое вы видите ниже.

Я пробовал различные способы выполнить следующие шаги в этом порядке, но безуспешно:

Повторяйте шаги 2 и 3, пока мышь не будет отпущена:

  1. Сохраните текущее изображение на холсте.
  2. Нарисуйте прямую линию от начальной точки до текущей позиции курсора.
  3. Восстановите предыдущее изображение, увиденное на шаге 1, на холсте при обнаружении движения мыши.
  4. После отпускания мыши нарисуйте последнюю прямую линию от начальной точки до текущей позиции курсора.
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();    
            }
        }
    }

};

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
59
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Я думаю, это потому, что 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();

спасибо, что выявили причину. Это устранило проблему.

jam 04.08.2024 14:00

чтобы ограничить перерисовку строки, я добавил ---> let [endX, endY] = [e.offsetX, e.offsetY]; после ctx.stoke(), а затем досрочно вернуться из функции рисования, если расстояние между (endX,endY) и e.offsetX, e.offsetY меньше, чем lineWidth.

jam 04.08.2024 14:09

Почему бы не сделать так, чтобы линия предварительного просмотра обновлялась динамически при движении мыши, а окончательная линия рисовалась бы точно, когда мышь отпущена.

<!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() был размещен неправильно.

jam 04.08.2024 14:02

...Как только мышь переместится в новое положение без отпускания, старая линия предварительный просмотр должен исчезнуть и должен появиться новый...

Не знаю, правильно ли я вас понял, но судя по изображению, ваша проблема в том, что новая строка не удаляет предыдущую и следовательно, как и на изображении, все линии у вас лежат на холсте.

Посмотрите на этот подход:

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 04.08.2024 14:03

@jam Я думал, ты именно это имел в виду в цитате из твоего вопроса, которую я привел в своем ответе. Посмотрите редактирование, если я хорошо понимаю ваши потребности.

Łukasz D. Mastalerz 04.08.2024 14:30

Другие вопросы по теме