Рисуем карту изображения мышкой

в 13:00, , рубрики: html, image, javascript, JS, map, метки: , ,

Привет. Давеча мне довелось иметь дело с такой специфической фичей html как карта изображения. Скажу честно, что мне не часто доводилось использовать её, и то, обычно, всё обходилось зонами в форме прямоугольника. Но это был не тот самый случай. Задачей было повесить ссылки на отдельные регионы изображения, которым выступала карта страны, и, к сожалению, ни о каких канвасах или svg не могло быть и речи. Только html только хардкор! Итак, задача поставлена, гугл активизирован, можно и начинать.

Теория

Начнём пожалуй с теории, куда ж без неё. Карта изображения содержит в себе два тега: map — контейнер карты и area — зона выделения. Карта не ограничена одной зоной и может содержать неограниченное их количество. Тег area кроме стандартных атрибутов имеет и свои собственные:

  • coords — координаты зоны выделения
  • href — ссылка, на которую будет произведён переход при клике на зону
  • nohref — указывает на то, что зона не содержит ссылки
  • shape — форма выделения
    • circle — зона выделения в виде круга
    • default — выделяет всю зону изображения
    • poly — зона выделения в виде многоугольника
    • rect — зона выделения в виде прямоугольника
  • target — определяет где будет открываться ссылка

Чтобы подключить карту к изображению, указываем тегу map атрибут name с произвольным именем, а на изображения вешаем тег usemap, значение которому указываем в формате "#имя".

Так как зона выделения у меня должна была быть многоугольной, значение атрибута shape, тега area, мы указываем как poly — полигональная область. В таком режиме через запятую указываются координаты точки относительно левого верхнего угла — x,y. Точки также разделяются запятыми, что по началу при чтении такого кода вызывает недоумение.

Рисуем карту изображения мышкой

<img src="sample.png" width="200" height="200" alt="Sample" usemap="#sample">
<map name="sample">
    <area shape="poly" coords="30,100,100,30,100,170,170,100">
</map>

Пишем Paint

Меня не тешила перспектива фотошопом находить координаты каждой точки, переписывать вручную цифры с окошка Info, а инструменты, которые попадались в гугле, были слишком монструозны. Было принято решения на коленке написать свой небольшой скрипт, который бы позволял расставлять точки просто кликая по нужной зоне на изображении, и выводил бы готовый код.

Для начала подготовим вёрстку:

<div class="canvas" id="container">
    <div class="inner" id="canvas">
        <img src="img/sample.jpg" width="934" height="407" alt="Sample">
    </div>
</div>
<div class="bar" id="bar"></div>
<div class="info" id="info"></div>

В #bar будут вставляться кнопки для управления «пеинтом».
В #info будет выводится сгенерированный html код.

CSS:

body {
    margin: 0;
    padding: 20px;
    font-family: Arial, Helvetica, sans-serif;
}
img {
    border: none;
    outline: none;
    display: block;
    -moz-user-select: none;
    -webkit-user-select: none;
    user-select: none;
}
.canvas {
    border: 2px solid #333;
    padding: 2px;
    margin-bottom: 16px;
    display: inline-block;
    //display: inline;
    //zoom:1;
}
.canvas.draw {
    border-color: #3C0;
}
.canvas .inner {
    position: relative;
}
.canvas .point {
    width: 1px;
    height: 1px;
    background-color: #fff;
    border: 1px solid #000;
    overflow: hidden;
    position: absolute;
}
.bar {
    margin-bottom: 16px;
}
.info {
    background-color: #FCFCFC;
    border: 1px dotted #CCC;
    font-size: 12px;
    font-style: italic;
    padding: 8px;
    word-wrap: break-word;
}

В javascript'е всё достаточно просто. В процессе написания я использовал свою боевую библиотечку, так что не удивляйтесь нестандартным функциям. Для начала повесим событие mousedown на #canvas, в котором будет рендериться точка на изображении и записываться её координаты.

var addPoint = function(e){
    var e = _.getEvent(e),
          offset = _.getOffset(nodes['canvas']),
          x = e.clientX + _.getDocScrollLeft() - offset[0],
          y = e.clientY + _.getDocScrollTop() - offset[1],
          node = nodes['canvas'].appendChild(_.node('div', {'class':'point'}));
			
    node.style.top = y-1+'px';
    node.style.left = x-1+'px';
			
    points.push({'x' : x, 'y' : y, 'node' : node});
		
    e.preventDefault && e.preventDefault();
    return false;
};

Затем напишем функцию, которая будет генерировать html код нашей карты.

var renderInfo = function(){
    var text;
		
    _.clearNode(nodes['info']);
		
    nodes['info'].appendChild(_.node('span', '<map>'));
    nodes['info'].appendChild(_.node('br'));
		
    for(var i = 0, l = areas.length; i < l; i++){
        if(areas[i].length > 0){
            text = '<area shape="poly" coords="';
            for(var i2 = 0, l2 = areas[i].length; i2 < l2; i2++){
                if(i2 > 0){
                    text += ',';
                }
                text += areas[i][i2]['x'] + ',' + areas[i][i2]['y'];
            }
            text += '">';
            nodes['info'].appendChild(_.node('span', text));
            nodes['info'].appendChild(_.node('br'));
        }
    }
		
    nodes['info'].appendChild(_.node('span', '</map>'));
};

Обрамим всё в класс, немного вспомогательных функций, вот и всё. Надеюсь кому-то будет полезна сея тулза.

Ссылки

Демо.
Сурсы (~195 KB).

Автор: SerDIDG

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js