Обнаружение наведения мыши на определенные точки на холсте HTML? - PullRequest
32 голосов
/ 03 августа 2009

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

Для простых гистограмм и диаграмм Гаанта, древовидных диаграмм и карт узлов с простыми квадратными областями или конкретными точками интереса я смог реализовать это путем наложения абсолютно позиционированных DIV с атрибутами: hover, но есть и более сложные визуализации, такие как как круговые диаграммы и рендеринг транспортного потока, который имеет сотни отдельных областей, определенных кривыми Безье.

Можно ли каким-либо образом прикрепить оверлей или вызвать событие, когда пользователь наведется на определенный закрытый путь?

Каждая область, для которой необходимо указать наведение, определяется следующим образом:

context.beginPath();
context.moveTo(segmentRight, prevTop);
context.bezierCurveTo(segmentRight, prevTop, segmentLeft, thisTop, segmentLeft, thisTop);
context.lineTo(segmentLeft, thisBottom);
context.bezierCurveTo(segmentLeft, thisBottom, segmentRight, prevBottom, segmentRight, prevBottom);
/*
 * ...define additional segments...
 */
// <dream> Ideally I would like to attach to events on each path:
context.setMouseover(function(){/*Show hover content*/});
// </dream>
context.closePath();

Привязка к объекту, подобному этому, почти тривиальна для реализации во Flash или Silverlight, поскольку текущая реализация Canvas имеет преимущество, заключающееся в непосредственном использовании нашего существующего API Javascript и интеграции с другими элементами Ajax, и мы надеемся не помещать Flash в смесь.

Есть идеи?

Ответы [ 7 ]

21 голосов
/ 03 августа 2009

Вы можете обработать событие mousemove и получить координаты x, y из события. Тогда вам, вероятно, придется перебирать все ваши пути, чтобы проверить, находится ли точка на пути. У меня была похожая проблема , в которой мог быть какой-то код, который вы могли бы использовать.

Циклы по таким вещам могут быть медленными, особенно в IE. Один из способов, которым вы могли бы потенциально ускорить его - и это хак, но это было бы весьма эффективно - было бы изменить цвет, которым нарисован каждый путь, чтобы он был незаметен для людей, но чтобы каждый путь был нарисован в другой цвет. Подготовьте таблицу, чтобы найти цвета для контуров и просто посмотрите цвет пикселя под мышью.

13 голосов
/ 07 июля 2011

Теневой холст

Лучший способ обнаружения наведения мыши, который я видел в других местах, - это повторить ту часть рисунка, которую вы хотите обнаружить, на скрытом очищенном холсте. Затем сохраните объект ImageData. Затем вы можете проверить массив ImageData на интересующий пиксель и вернуть значение true, если значение альфа больше 0.

// slow part
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.fillRect(100,100,canvas.width-100,canvas.height-100);
var pixels = ctx.getImageData(0,0,canvas.width,canvas.height).data;

// fast part
var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
if (pixels[idx]) { // alpha > 0
  ...
}

Преимущества

  • Вы можете обнаружить все, что захотите, поскольку вы просто повторяете методы контекста. Это работает с PNG альфа, сумасшедшими сложными фигурами, текстом и т. Д.
  • Если ваше изображение довольно статично, то вам нужно сделать это только один раз для каждой области интереса.
  • «Маска» медленная, но смотреть на пиксель очень дешево. Таким образом, «быстрая часть» отлично подходит для обнаружения наведения мыши.

Недостатки

  • Это боров памяти. Каждая маска имеет значения W * H * 4. Если у вас есть небольшая область холста или несколько областей для маскировки, это не так уж плохо. Используйте диспетчер задач Chrome для мониторинга использования памяти.
  • В настоящее время существует известная проблема с getImageData в Chrome и Firefox. Результаты не сразу собираются, если вы обнуляете переменную, поэтому, если вы будете делать это слишком часто, вы увидите, что память будет быстро расти. В конечном итоге он собирает мусор и не должен вызывать сбой браузера, но может облагаться налогом на компьютерах с небольшим объемом оперативной памяти.

Взлом для экономии памяти

Вместо того, чтобы хранить весь массив ImageData, мы можем просто запомнить, какие пиксели имеют альфа-значения. Это экономит много памяти, но добавляет цикл к процессу маскировки.

var mask = {};
var len = pixels.length;
for (var i=3;i<len;i+=4) if ( pixels[i] ) mask[i] = 1;

// this works the same way as the other method
var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
if (mask[idx]) {
  ...
}
7 голосов
/ 03 августа 2009

Это можно сделать с помощью метода ctx.isPointInPath, но это не реализовано в ExCanvas для IE. Но другое решение было бы использовать карты HTML, как я сделал для этой маленькой библиотеки: http://phenxdesign.net/projects/phenx-web/graphics/example.htm вы можете получить от нее вдохновение, но она все еще немного глючит.

1 голос
/ 13 марта 2012

Есть книга Эрика Роуэлла "HTML5 CANVAS COOKBOOK". В этой книге есть глава под названием «Взаимодействие с Canvas: присоединение слушателей событий к фигурам и областям». Могут быть реализованы события mousedown, mouseup, mouseover, mouseout, mousemove, touchstart, touchend и touchmove. Я настоятельно рекомендую вам прочитать это.

1 голос
/ 20 декабря 2011

Мне нужно было обнаружить щелчки мыши для сетки квадратов (например, ячеек таблицы Excel). Чтобы ускорить его, я разделил сетку на области, рекурсивно делимые пополам, до тех пор, пока не осталось небольшое количество ячеек, например, для сетки 100x100, первые 4 области могли быть сетками 50x50, содержащими четыре квадранта. Затем они могут быть разделены на еще 4 каждый (следовательно, давая 16 областей 25x25 каждый). Это требует небольшого количества сравнений, и, наконец, сетка 25x25 может быть протестирована для каждой ячейки (625 сравнений в этом примере).

1 голос
/ 08 сентября 2010

Я бы предложил наложить карту изображения с правильными координатами, установленными в областях, соответствующих вашим нарисованным на холсте объектам. Таким образом, вы получаете подсказки и множество других функций DOM / Browser бесплатно.

0 голосов
/ 03 августа 2009

Это невозможно сделать (ну, по крайней мере, не так просто), потому что объекты, которые вы рисуете на холсте (пути), не представлены как те же самые объекты на холсте. Я имею в виду, что это простой 2D-контекст, и как только вы на нем что-то рисуете, он полностью забывает, как он был нарисован. Для него это просто набор пикселей.

Чтобы наблюдать за наведением мыши и тому подобным, вам нужен какой-то холст с векторной графикой, то есть SVG, или реализовать свой собственный поверх существующего (что и предложил Сэм Хаслер)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...