РИТ: Клиентские технологии - профессиональная конференция веб-разработчиков

Удобное создание ролловеров на картах: imagemap + canvas

Чтобы заставить работать ролловеры на карте изображений (imagemap), приходится создавать множество дополнительных рисунков, заменяя ими основной. Для некоторых задач этого можно избежать, воспользовавшись canvas.

Курс молодого бойца

Ролловер (rollover) — это интерактивный графический элемент, к примеру изображение, которое начинает изменяться при наведении на него курсора мыши или при клике. Как правило ролловеры представляют из себя несколько изображений, сменяемых яваскриптом (или через CSS) при определенных событиях. В нашем случае необходимо, чтобы при наведении курсора мыши на участок имэджмапы менялся внешний вид только этого участка, не затрагивая карту в целом. Слабо?

О том, что такое карты изображений, и как их создавать, читаем здесь. Также предварительно знакомимся с canvas и методом "рисования" многоугольников.

Начинаем

Итак, мы умеем рисовать карты изображений (HTML) и создавать совершенно произвольные многоугольники (Canvas + JavaScript). Объединяем эти знания и получим карту, на которой можно показывать ролловеры по её отдельным областям. Одолеем простую задачу: затемним участок карты изображения при наведении курсора мыши.

Для этого в верстке будем использовать модель пирога со следующими слоями:

  1. Верхний: прозрачный ГИФ, к которому привязана имеджмапа
  2. Средний: блок Canvas
  3. Нижний: Само изображение с картой.

Естественно, ширина и высота всех слоев равна одна другой.

Смотрим HTML & CSS:

canvas {position:absolute; top:10px; left:10px; z-index:20;}
img {position:absolute; top:10px; left:10px; border:0;}
.first {z-index:30;}
.last {z-index:10;}
<!-- Выводим тот самый слоеный пирог -->
<img class="first" src="http://fastcoder.org/i/0.gif" width="220" height="140" alt="" usemap="#map1">
<canvas id="canvasId" width="220" height="140"></canvas>
<img class="last" src="http://fastcoder.org/i/pribaltika.gif" width="220" height="140" alt="">

<!-- Разметка имэджмапы -->
<map name="map1" id="imageMapId">
    <area shape="poly" alt="Эстония" coords="144,44, 136,47, 136,51, 145,54, 143,68, 141,73, 142,82, 144,90, 149,93, 149,100, 157,100, 161,102, 166,100, 174,91, 183,88, 183,86, 187,83, 208,83, 208,85, 214,85, 217,80, 217,78, 210,76, 205,69, 206,62, 198,43, 198,40, 193,39, 192,30, 186,28, 186,25, 181,24, 181,21, 177,20, 171,14, 168,15, 165,13, 163,17, 158,17, 154,21, 154,24, 156,24, 156,25, 153,25, 144,29, 145,34, 143,39, 151,41, 149,44" href="#Эстония">
    <area shape="poly" alt="Латвия" coords="52,12, 43,16, 52,29, 58,30, 58,36, 56,51, 58,60, 63,61, 67,70, 67,72, 66,75, 71,74, 74,88, 84,106, 84,115, 82,120, 86,123, 90,123, 92,127, 94,133, 100,133, 108,135, 115,137, 128,126, 128,124, 131,123, 131,116, 139,115, 146,115, 146,108, 149,104, 149,93, 144,90, 142,82, 141,73, 143,68, 145,54, 136,51, 136,47, 130,48, 114,57, 108,57, 99,50, 100,39, 107,34, 112,21, 115,21, 115,16, 110,13, 104,6, 89,3, 81,7, 71,3, 63,7, 54,9" href="#Латвия">
    <area shape="poly" alt="Литва" coords="13,80, 13,70, 9,65, 13,63, 13,60, 19,59, 25,59, 31,54, 30,51, 25,43, 26,30, 29,21, 39,16, 43,16, 52,29, 58,30, 58,35, 56,50, 58,60, 63,61, 67,70, 67,72, 66,75, 71,74, 74,87, 84,106, 84,115, 82,120, 77,121, 73,116, 68,117, 57,120, 48,120, 38,115, 34,116, 31,118, 29,118, 29,110, 30,107, 26,105, 10,105, 7,99, 8,93, 3,92, 3,86" href="#Литва">
</map>

Работа для JavaScript

Нам необходимо знать, над какой областью проводит в данным момент пользователь курсором мыши. Для этого "прикрутим" с помощью функции initImageMap обработчики событий mouseover & mouseout для элементов area. Одновременно кешируем в массив координаты. Для определения поддержки клиентским браузером canvas-а воспользуемся функцией canvasBrowser:

function canvasBrowser() {
    // Определяем тип браузера
    var ua = navigator.userAgent.toLowerCase();
    var isIE = (ua.indexOf("msie") != -1 && ua.indexOf("opera") == -1 && ua.indexOf("webtv") == -1);
    var isOpera = ua.indexOf("opera") != -1;
    var isFF = ua.indexOf("firefox") != -1;
   
    var result = false;
    // Разрешим все версии IE
    if (isIE) result = true;
    // Лису разрешим только с полторашки
    else if (isFF) {
        var ffVersion = parseFloat(ua.substring(ua.indexOf("firefox") + 8, ua.length));
        if (ffVersion >= 1.5) result = true;
    // Оперу, начиная с версии 9.0
    } else if (isOpera) {
        var operaVersion = parseFloat(ua.substring(ua.indexOf("opera") + 6, ua.length));
        if (operaVersion >= 9.0) result = true;
    }
    return result;
}

// Кеш координат
var coordsCashe = [];
// Прикручиваем события
function initImageMap() {
    if (!canvasBrowser()) return;
    var map = document.getElementById("imageMapId");
    var area, i;
    for (i = 0; i < map.childNodes.length; i++) {
        area = map.childNodes[i];
        // Проверяем тип узла
        if (area.nodeType != 1) continue;
        // Проверяем, что узел является элементом area
        if (area.nodeName.toLowerCase() != "area") continue;
        // Добавляем ID c ключом массива координат
        area.id = "id" + i;
        // Добавляем к элементу обработчики событий
        area.onmouseover = mouseOverHandler;
        area.onmouseout = mouseOutHandler;
        // Кешируем координаты
        coordsCashe[i] = parseCoords(area.coords);
    }
}

// Обработчик события mouseover
function mouseOverHandler() {
    // Вырезаем индекс для массива координат из ID
    var i = this.id.substring(2, this.id.length);
    // Рисуем многоугольник на полученной области
    drawPoly(
        "canvasId",
        coordsCashe[i]
    );
}
// Обработчик события mouseout
function mouseOutHandler() {
    // Стираем нарисованное в canvas
    clearCanvas("canvasId");
}

// Парсим строку с координатами, перечисленными через запятую, в двумерный массив
function parseCoords(str) {
    var coords = [];
    var buferArray = str.split(",");
    var j = 0;
    for (var i = 0; i < buferArray.length; i++) {
        if (i % 2 == 0) {
            coords[j] = [];
            coords[j][0] = buferArray[i];
        } else {
            coords[j][1] = buferArray[i];
            j++;
        }
    }
    return coords;
}

Теперь функции, занимающиеся отрисовкой многоугольников и их стиранием в canvas:

// Функция, принимающая id тега <canvas> и массив координат
function drawPoly(id, arr) {
    var canvas = document.getElementById(id).getContext('2d');
    // Начинаем отрисовку
    canvas.beginPath();
    for (var i = 0; i < arr.length; i++) {
        // Ставим точку на исходную позицию
        if (i == 0) canvas.moveTo(arr[i][0], arr[i][1]);
        // Рисуем линии от точки к точке
        else canvas.lineTo(arr[i][0], arr[i][1]);
    }
    // Задаем цвет заливки в формате RGBA
    canvas.fillStyle = "rgba(128,128,128,0.7)";
    // Зальем полученный многоугольник цветом
    canvas.fill();
}

// Очищаем область Canvas
function clearCanvas(id) {
    var canvas = document.getElementById(id)
    var width = parseInt(canvas.width);
    var height = parseInt(canvas.height);
    canvas = canvas.getContext('2d');
    canvas.clearRect(0, 0, width, height);
}

Дело за малым — собрать все в один HTML-файл. Смотрим пример работы ролловеров в картах изображений с помощью canvas.

Проверено в:

  • IE 6;
  • Mozilla Firefox 1.5;
  • – Opera 9.01+;

Александр Бурцев 31 августа 2008

© Все права на данную статью принадлежат порталу fastcoder.org. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в печатных изданиях допускается только с разрешения редакции.

Комментарии

osmosis 19 ноября 2008, 13:49 #
Спасибо большое за статью, очень помогла. Не подскажете, как сделать так, чтобы на такой карте, при неведении мыши на область, не только менялся цвет, но и появлялась текстовая информация (рядом с картой, где-нибудь в отдельной строке)?
 
Bur 19 ноября 2008, 13:58 #
Удобнее всего, имхо, поместить текстовую информацию (если он без HTML-разметки и в одну строку) в атрибут alt тегов <area> имеджмапы. В моем примере там для трех областей прописано название гос-в.

Далее, у вас есть контейнер, в который планируется выводить подсказку:
<div id="helpId"></div>

Тогда в обработчике mouseOverHandler дописываем в самом начале строку:
document.getElementById("helpId").innerHTML = this.alt;

а в обработчике mouseOutHandler:
document.getElementById("helpId").innerHTML = '';

Сам не проверял, но должно работать.
 
osmosis 20 ноября 2008, 16:21 #
Спасибо за совет. Работает как часы, но мне надо бы именно с HTML-разметкой, т.к. там инфы много должно вылезать. И еще один вопрос: как сделать, чтобы ролловер был не заливкой цветом, а область эта заменялась на такое же изображение, только более яркое. Или, если есть такой скрипт, чтобы он делал карту полупрозрачной, а при наведении курсора, нужная область становилась полноцветной?
 
 
Rambler's Top100 Flede HTML valid CSS valid Технологический конкурс сайтов WebHiTech