Я считаю, что каждый должен был сталкиваться с такой необходимостью при изучении холста или использовании холста при разработке проектов: реализовать гаджет блокнота, который можно писать.
Что ж, я считаю, что для детей, которые лучше знакомы с холстом, это можно сделать всего с помощью нескольких десятков строк кода. Простым примером является следующая демонстрация:
<!DOCTYPE html><html><head> <title>Демо-версия Sketchpad</title> <style type=text/css> Canvas { border: 1px blue Solid } </style></head><body> <canvas id=ширина холста=800 высота=500></canvas> <тип сценария=текст/javascript> let isDown = false; let BeginPoint = null Canvas =; document.querySelector('#canvas'); const ctx = Canvas.getContext('2d'); // Установить цвет линии ctx.strokeStyle = 'red'; ctx.lineWidth = 1; ctx.lineJoin = 'ctx'; .lineCap = 'круглый'; холст.addEventListener('mousedown', вниз, false); Canvas.addEventListener('mousemove', move, false); Canvas.addEventListener('mouseup', up, false); Canvas.addEventListener('mouseout', up, false); function down(evt) { isDown = true; BeginPoint = getPos(evt) function; move (evt) { if (!isDown) return; const endPoint = getPos (evt); drawLine (beginPoint, endPoint); BeginPoint = endPoint } function; up(evt) { if (!isDown) return; const endPoint = getPos(evt); drawLine(beginPoint, endPoint); BeginPoint = null; isDown = false; } function getPos(evt) { return { x: evt.clientX, y: evt.clientY } } function drawLine(beginPoint, endPoint) { ctx.beginPath(); ctx.moveTo(beginPoint.x, BeginPoint.y); ctx.lineTo(endPoint.x, endPoint.y); ctx.stroke(); } </script></body></html; >
Логика его реализации также очень проста:
mousedown
, mouseup
и mousemove
, а также создаем переменную isDown
;mousedown
), установите для isDown
значение true
, а когда пользователь опускает мышь ( mouseup
), установите значение false
. Преимущество этого подхода заключается в том, что он может определить, находится ли пользователь в данный момент в состоянии рисования. ;mousemove
. Если и только если isDown
имеет true
(то есть в состоянии записи), текущая точка будет соединена и нарисована с предыдущей точкой через метод lineTo
. холст;С помощью вышеописанных шагов мы можем реализовать основную функцию чертежной доски. Однако все не так просто. Бережная детская обувь может столкнуться с очень серьезной проблемой – линии, нарисованные таким образом, будут неровными и недостаточно гладкими, и тем быстрее вы будете. Рисуйте, тем сильнее будет ощущение ломаных линий. Производительность показана ниже:
Почему это происходит? Анализ проблемыОсновными причинами этого явления являются:
Мы соединяем точки, используя метод холста lineTo
. Две соседние точки соединяет прямая линия, а не кривая, поэтому то, что рисуется таким образом, является полилинией;
Ограниченная частотой сбора браузером событий mousemove
, все знают, что во время mousemove
браузер собирает координаты текущей мыши каждый короткий период времени. Следовательно, чем быстрее движется мышь, тем расстояние между двумя соседними собранными точками будет тем дальше. вдали, тем очевиднее ощущение линий сгиба;
На самом деле есть способы рисовать плавные кривые. Если lineTo
ненадежен, мы можем использовать другой API рисования Canvas quadraticCurveTo
, который используется для рисования квадратичных кривых Безье.
квадратичная кривая Безье
quadraticCurveTo(cp1x, cp1y, x, y)
Для вызова quadraticCurveTo
требуются четыре параметра cp1x
и cp1y
, описывающие контрольные точки, а x
и y
— конечные точки кривой:
Более подробную информацию можно найти на MDN.
Поскольку мы хотим использовать кривую Безье, очевидно, что наших данных недостаточно. Чтобы полностью описать квадратичную кривую Безье, нам нужны: начальная точка, контрольная точка и конечная точка. Откуда берутся эти данные?
Существует очень умный алгоритм, который может помочь нам получить эту информацию.
Алгоритм получения квадратичных ключевых точек БезьеЭтот алгоритм не сложен для понимания. Приведу прямой пример:
Предположим, мы собрали в общей сложности 6 координат мыши на картине, а именно A, B, C, D, E, F
, берем предыдущие три точки A, B, C
, вычисляем среднюю точку B1
из B
и C
и используем A
is; начальная точка, B
— контрольная точка, B1
— конечная точка. ИспользуйтеquadaticCurveTo quadraticCurveTo
чтобы нарисовать квадратичный сегмент кривой Безье;
Затем вычислите среднюю точку C1
между точками C
и D
и продолжайте рисовать кривую, используя B1
в качестве начальной точки, C
в качестве контрольной точки и C1
в качестве конечной точки;
Рисование продолжается по аналогии. Когда достигается последняя точка F
, кривая Безье заканчивается D1
, средней точкой D
и E
, в качестве начальной точки, E
в качестве контрольной точки и F
в качестве конечной точки.
Хорошо, алгоритм такой, дальше будем модернизировать существующий код на основе этого алгоритма:
let isDown = false;let Points = [];let BeginPoint = null;const Canvas = document.querySelector('#canvas');const ctx = Canvas.getContext('2d');//Установить цвет линии ctx.strokeStyle = 'красный';ctx.lineWidth = 1;ctx.lineJoin = 'круглый';ctx.lineCap = 'round';canvas.addEventListener('mousedown', down, false);canvas.addEventListener('mousemove', move, false);canvas.addEventListener('mouseup', up, false);canvas.addEventListener('mouseout' , вверх, ложь); функция вниз (evt) { isDown = true; const { x, y } = getPos (evt); points.push({x, y}); BeginPoint = {x, y};}function move(evt) { if (!isDown) return; const { x, y} = getPos(evt); x, y}); if (points.length > 3) { const LastTwoPoints = Points.slice(-2); const controlPoint = LastTwoPoints[0]; const endPoint = { x: (lastTwoPoints[0].x + LastTwoPoints[1].x) / 2, y: (lastTwoPoints[0].y + LastTwoPoints[1].y) / 2, } drawLine(beginPoint, controlPoint, endPoint); BeginPoint = endPoint; }} function up (evt) { if (!isDown) return; const { x, y } = getPos (evt); Points.push({x, y}); if (points.length > 3) { const LastTwoPoints = Points.slice(-2); const controlPoint = LastTwoPoints[0]; const EndPoint = LastTwoPoints[1]; , controlPoint, endPoint); } BeginPoint = null; isDown = false; точки = [];} function getPos (evt) { return { x: evt.clientX, y: evt.clientY }} function drawLine(beginPoint, controlPoint, endPoint) { ctx.beginPath(); ctx.moveTo(beginPoint.x, BeginPoint.y); ctx.quadraticCurveTo(controlPoint.x, controlPoint. y, endPoint.x, endPoint.y); ctx.stroke(); ctx.closePath();}
На основе оригинала мы создали переменную points
для сохранения точек, через которые прошла мышь в предыдущем событии mousemove
. Согласно этому алгоритму, для рисования квадратичной кривой Безье требуется не менее 3 точек, поэтому у нас есть только точки в points
Розыгрыш начинается только тогда, когда количество точек превышает 3. Последующая обработка точно такая же, как и этот алгоритм, поэтому я не буду здесь вдаваться в подробности.
После обновления кода наша кривая стала намного более плавной, как показано на рисунке ниже:
На этом статья заканчивается. Надеюсь, вам всем понравится рисовать на холсте~ Увидимся в следующий раз :)
Если вас интересует детская обувь, вы можете нажать здесь, чтобы подписаться на мой блог. Любые новые и интересные сообщения в блоге будут публиковаться здесь как можно скорее ~.
Выше приведено все содержание этой статьи. Я надеюсь, что она будет полезна для изучения всеми. Я также надеюсь, что все поддержат сеть VeVb Wulin.