Внутренние элементы холста не могут добавлять интерактивные прослушиватели событий так же удобно, как элементы DOM, потому что в холсте нет концепции элементов, это просто графика, нарисованная холстом. Это необходимое препятствие для интерактивной разработки. Идея мониторинга события щелчка изображения очень проста. Просто слушайте событие щелчка самого элемента холста, а затем определите, внутри какого изображения расположены координаты щелчка. тогда событие графического щелчка может быть скрыто реализовано. В этой статье будут представлены три метода определения того, находится ли точка координат внутри изображения холста.
СоглашениеТри метода, представленные в этой статье, подходят для идентификации графических событий щелчков неправильной формы и неправильного положения на холсте. Для сцен с правильными формами или регулярными позициями необходимы более простые реализации, которые здесь не будут обсуждаться.
пиксельный методИдея метода обнаружения пикселей состоит в том, чтобы отдельно отрисовать несколько изображений на холсте (если их несколько) за кадром и использовать метод getImageData() для получения данных пикселей и их сохранения. Когда элемент холста прослушивает событие щелчка, координаты щелчка можно использовать для непосредственного расчета пикселя на холсте, где произошел щелчок, а затем просмотреть ранее сохраненные графические данные, чтобы увидеть, равно ли значение альфа этого пикселя 0. Если это 0, это означает, что точка приземления находится за пределами текущей фигуры, в противном случае это означает, что точка достигла этой фигуры.
Метод для получения номера пикселя, на который нажали, на основе координат клика:
Число пикселей = (ордината-1) * ширина холста + абсцисса
Например, если щелкнуть координаты (3,3) на холсте шириной 5, то количество пикселей, полученное по приведенной выше формуле, составит (3-1) * 5 + 3 = 18, как показано на рисунке:
Поскольку графические данные, экспортированные холстом, хранят каждый пиксель в массиве из 4 чисел в порядке rgba, поэтому, если вы хотите получить доступ к альфа-значению указанного пикселя, вам нужно только прочитать значение pIndex * 4 + 3 этого массива. , если это значение не равно 0, это означает, что пиксель виден, то есть на графике щелкнули.
Я считаю, что этот метод имеет наиболее прямые идеи и наиболее точные результаты и не предъявляет никаких требований к форме графики. Однако у этого метода есть фатальное ограничение, когда графику необходимо перемещать по холсту. , кэши данных должны создаваться часто, чтобы гарантировать точность результатов. Из-за размера холста и количества графики производительность метода getImageData() становится серьезным узким местом. Поэтому, если изображение на холсте статично, этот метод очень подходит, в противном случае его использовать нецелесообразно.
Угловой методПринцип метода оценки угла легко понять. Если точка находится внутри многоугольника, угол между точкой и всеми вершинами многоугольника должен составлять ровно 360°.
Процесс расчета можно преобразовать в следующие три этапа:
1. Учитывая известные вершины многоугольника и известные координаты, объедините координаты и вершины попарно, чтобы сформировать очередь из трех точек.
2. Чтобы найти угол между тремя известными точками, можно воспользоваться теоремой Юй Сюаня.
3. Определите, равна ли сумма входящих в него углов 360°.
Каждый шаг прост и реализуется следующим образом:
//Рассчитываем расстояние между двумя точками const getDistence = function (p1, p2) { return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2. y) * (p1.y - p2.y))};//Метод angular определяет, что точка находится внутри многоугольника const checkPointInPolyline = (point,polylinePoints) => { let totalA = 0; const A = point; for (let i = 0; i <polylinePoints.length; i++) { let B, C; if (i === polylinePoints.length - 1) { B = { x: polylinePoints[i][0 ], y: PolylinePoints[i][1] }; C = { x: PolylinePoints[0][0], y: PolylinePoints[0][1] } else { B = { x: PolylinePoints[i][0], y: PolylinePoints[i][1] }; C = { x: PolylinePoints[i + 1][0], y: PolylinePoints[i + 1][1] }; } //Вычисляем угол const angularA = Math.acos((Math.pow(getDistence(A, C), 2) + Math.pow(getDistence(A, C), 2) + Math.pow(getDistence(A, B), 2) - Math.pow(getDistence(B, C), 2)) / (2 * getDistence(A, C) * getDistence(A, B))) totalA += angularA } // Оцениваем сумму углы возвращают totalA === 2 * Math.PI}Одним из ограничений этого метода является то, что фигура должна представлять собой выпуклый многоугольник. Если многоугольник не является выпуклым, перед расчетом его необходимо разрезать на выпуклый многоугольник, что сложнее.
Похожей идеей является метод площади. Если точка находится внутри многоугольника, то сумма площадей треугольника, образованного точкой и всеми вершинами многоугольника, должна быть равна площади многоугольника. очень хлопотно сначала вычислить площадь многоугольника, поэтому этот метод можно передать напрямую.
Лучевой методМетод лучей — это метод, который я не могу объяснить ясно, но он очень прост в использовании. Пока количество точек пересечения точки и одной стороны многоугольника является нечетным числом, точка находится внутри многоугольника. Обратите внимание, что вам нужно посчитать количество точек фокусировки только на любой стороне, например, на левой стороне. Этот метод не ограничивает тип многоугольника: допустимы выпуклые многоугольники, вогнутые многоугольники или даже кольца.
Это также очень просто реализовать:
const checkPointInPolyline = (point, polylinePoints) => { // Метод луча let leftSide = 0; const A = point; for (let i = 0; i <polylinePoints.length; i++) { let B, C; if (i = == PolylinePoints.length - 1) { B = { x: PolylinePoints[i][0], y: PolylinePoints[i][1] }; PolylinePoints[0][0], y: PolylinePoints[0][1] } else { B = { x: PolylinePoints[i][0], y: PolylinePoints[i][1] }; : polylinePoints[i + 1][0], y: polylinePoints[i + 1][1] } //Оцениваем левое пересечение let sortByY = [By, Cy].sort((a,b) => ab) if (sortByY[0] < Ay && sortByY[1] > Ay){ if(Bx<Ax || Cx < Ax){ leftSide++ } } } return leftSide % 2 === 1}Существует частный случай лучевого метода, требующий специального подхода, когда точка находится на ребре кратных деформаций. А вот в инженерии, я думаю, это не нужно обрабатывать, потому что если пользователь случайно нажмет на границу графики, то программа думает, что он не нажал и можно пройти.
Подвести итогВсе три вышеупомянутых метода могут реализовать обнаружение щелчков нестандартной графики на холсте. Среди них преимуществом пиксельного метода является то, что он не выбирает формы и имеет определенные преимущества в производительности в статических сценах; следует сказать, что угловой метод имеет только теоретическую ценность и низкую практичность, но наиболее практичным в инженерии является лучевой метод; , который имеет мало ограничений и прост в реализации. В большинстве случаев вам просто нужно знать метод луча.