[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: jQuery outline - вывод контура изображения png
ИНСИ
Часто в верстке стоит задача обрабатывать действие пользователя по контуру самого изображения, а не размерам картинки. Для этой задачи, я написал небольшой функционал для определения границ изображения под jQuery.

Код JS (модуль jQuery)
 
"use strict";

/**
* Автор : ИНСИ
* Версия : 0.1.0
*
* Плагин : Рисует контур изображений (с прозрачным фоном) и вешает обработчика при наведении на контур изображения
*/

(function ($) {

/**
* Внутренний объект, с необходимым функционалом для работы модуля
*/

var LibOutLine = {

/**
* Конфиги по умолчанию
*/

Config: {
OutLine: {
Color: 'red',
Width: 3
},
Contour: {
Dx: [1, 0, 1, 1, -1, 0, -1, 1, 0, 0, 0, 0, -1, 0, -1, NaN],
Dy: [0, -1, 0, 0, 0, -1, 0, 0, 1, -1, 1, 1, 0, -1, 0, NaN]
},
Error: {
1: 'Необходимо обновить браузер, иначе модуль не сможет выполнить свои задачи',
2: 'Контур может быть высчитан только для картинки'
}
}
,

/**
* Текущие свойства
*/

Current: {
Id: 1
},

/**
* Имена ID, Class элементов
*/

ElName: {
Key: {
Map: 'map_',
Area: 'area_',
Canvas: 'canvas_',
Container: 'container_'
}
}
,

/**
* Возвращает стартовые координаты для замыкания
*
* @param {function} grid
*
* @returns {*[]}
*/

StartPoints: function (grid) {

// Объявление переменных стартовых координат X, Y
var x = 0, y = 0;

// Ищем стартовые координаты
while (true) {

// Если найдены стартовые координаты в сетке данных
if (grid(x, y)) {

// Возвращаем координаты
return [x, y];
}

// Если первый цикл
if (x === 0) {

// Определяем следуюшую координату X
x = y + 1;

// Определяем следуюшую координату Y
y = 0;

// Цикл не первый
} else {

// Определяем следуюшую координату X
x = x - 1;

// Определяем следуюшую координату Y
y = y + 1;
}
}
}
,

/**
* Возвращает контур картинки
*
* @param {function} grid
*
* @returns {{points: Array, stroke: Array}}
*/

OutLine: function (grid) {

// Массив с результатом
var result = {

// Массив точек
points: [],

// Массив с координатами
stroke: []
};

// Находим стартовые точки контура
var startPoints = this.StartPoints(grid);

// Запоминаем стартовые позиции
var x = startPoints[0], y = startPoints[1];

// Определяем необходимые переменные для высчитывание контура
var dx = 0, dy = 0, pdx = NaN, pdy = NaN, i = 0;

// Высчитываем контур
do {

// Обнуляем позицию
i = 0;

// Определяем позицию
if (grid(x - 1, y - 1)) {
i += 1;
}

// Определяем позицию
if (grid(x, y - 1)) {
i += 2;
}

// Определяем позицию
if (grid(x - 1, y)) {
i += 4;
}

// Определяем позицию
if (grid(x, y)) {
i += 8;
}

// Определяем, является ли точка контуром
if (i === 6) {
dx = pdy === -1 ? -1 : 1;
dy = 0;
} else if (i === 9) {
dx = 0;
dy = pdx === 1 ? -1 : 1;
} else {
dx = this.Config.Contour.Dx[i];
dy = this.Config.Contour.Dy[i];
}

// Если точка является контуром
if (dx != pdx && dy != pdy) {

// Добавляем в результат
result.points.push([x, y]);

// Значение для координат
result.stroke.push(x + ',' + y);

// Сохраняем найденную позицию координаты X
pdx = dx;

// Сохраняем найденную позицию координаты Y
pdy = dy;
}

// Новое значение для координаты X
x += dx;

// Новое значение для координаты Y
y += dy;

// Пока не дойдем до стартовых точек
} while (startPoints[0] != x || startPoints[1] != y);

// Возвращаем результат
return result;
},

/**
* Рисует контур на теге <canvas />
*
* @param {object} canvasContext
* @param {object} points
*/

Draw: function (canvasContext, points) {

// Создаем новый холст для новой серии отрисовки
canvasContext.beginPath();

// Перемещаем "курсор" в позицию x, y
canvasContext.moveTo(points[0][0], points[0][1]);

// Обходим все точки
for (var i = 1; i < points.length; i++) {

// Рисуем контур
canvasContext.lineTo(points[i][0], points[i][1]);
}

// Завершаем задачу рисования
canvasContext.closePath();

// Рендерим
canvasContext.stroke();
},

/**
* Проверяет возможности браузера для высчитывания контура
*
* @returns {boolean}
*/

isSupported: function () {

// Создаем тег <canvas />
var canvas = document.createElement('canvas');

// Возвращаем результат
return !!(canvas.getContext && canvas.getContext('2d'));
}
}
;

/**
* Дополняем jQuery модулем .outline
*/

$.fn.outline = function (options, callBack) {

// Если первым параметром был передан callBack
if (typeof options === 'function') {

// Присваиваем callBack в "предназначенную" переменную :-)
callBack = options;
}

// Переменная с ошибкой
var err = false;

// Дожидаемся загрузки файла
this.on('load', function () {

// Объявляем необходимые данные
var container, canvas, mapTag, areaTag;

// Проверяем поддержку выполнение функции браузером
if (!LibOutLine.isSupported()) {

// Указываем описание ошибки
err = LibOutLine.Config.Error[1];
}

// Проверяем тип элемента
if ((err === false) && ($(this).is('img')) && (typeof $(this).attr('outLineId') !== 'number')) {

// Объявляем необходимые данные
var img = this, imgWidth = img.width, imgHeight = img.height, data, currentId = (++LibOutLine.Current.Id), keyMap = (LibOutLine.ElName.Key.Map + currentId);

// Добавляем атрибут usemap, для связи с тегом <map />
$(img)

// CSS свойства
.css('position', 'absolute')

// Указываем атрибуты
.attr({

// Ссылаемся на тег <map />
usemap: ('#' + keyMap),

// ID номер контура
outLineId: currentId
});

// Создаем контейнер
container = $('<span />')

// CSS свойства
.css({
position: 'relative'
})

// Добавляем в контейнер саму картинку
.append(img.outerHTML);

// Создаем тег <canvas />
canvas = $('<canvas />')

// Указываем атрибуты
.attr({

// Устанавливаем ширину
width: imgWidth,

// Устанавливаем высоту
height: imgHeight
})

// Указываем CSS свойства
.css({
display: 'none',
pointerEvents: 'none',
position: 'absolute',
zIndex: '999',
top: 0,
textAlign: 'center'
})

// Добавляем в контейнер
.appendTo(container);

// Получаем свойства
var canvasContext = $(canvas)[0].getContext("2d");

// Заново создаем изображение
canvasContext.drawImage(img, 0, 0);

// Получаем данные свойств изображения
data = (canvasContext.getImageData(0, 0, imgWidth, imgHeight)).data;

// Получаем контур картинки
var coords = LibOutLine.OutLine(function (x, y) {

return ((data[(y * imgWidth + x) * 4 + 3]) > 20);
});

// Добавляем тег <map /> в контейнер
mapTag = $('<map />')

// Указываем атрибуты
.attr({

// Ссылка на атрибут usemap у файла
name: keyMap
})

// Добавляем тег в контейнер
.appendTo(container);

// Если через опции передали слушателей на действия
if (typeof options === 'object' && typeof options.onEvent === 'object') {

// Вешаем слушателей
mapTag.on('click mouseenter mouseleave', function (e) {

// "Клик" по тегу <map />
if (e.type == 'click') {

// Если передали функцию
if (typeof options.onEvent.click === 'function') {

// Выполняем функцию и передаем все объекты
options.onEvent.click(err, container, canvas, mapTag, areaTag);

// Если при "клике" надо показать контур
} else if (options.onEvent.click === true) {

// Показываем тег <canvas />
canvas.css('display', 'inline-block');

// Если при "клике" надо убрать контур
} else if (options.onEvent.click === true) {

// Прячем тег <canvas />
canvas.hide();
}

// Курсор находится над контуром
} else if (e.type == 'mouseenter') {

// Если передали функцию
if (typeof options.onEvent.mouseenter === 'function') {

// Выполняем функцию и передаем все объекты
options.onEvent.mouseenter(err, container, canvas, mapTag, areaTag);

// Если при "клике" надо показать контур
} else if (options.onEvent.mouseenter == true) {

// Показываем тег <canvas />
canvas.css('display', 'inline-block');

// Если при "клике" надо убрать контур
} else if (options.onEvent.mouseenter == false) {

// Прячем тег <canvas />
canvas.hide();
}

// Курсор покинул контур
} else if (e.type == 'mouseleave') {

// Если передали функцию
if (typeof options.onEvent.mouseleave === 'function') {

// Выполняем функцию и передаем все объекты
options.onEvent.mouseleave(err, container, canvas, mapTag, areaTag);

// Если при "клике" надо показать контур
} else if (options.onEvent.mouseleave == true) {

// Показываем тег <canvas />
canvas.css('display', 'inline-block');

// Если при "клике" надо убрать контур
} else if (options.onEvent.mouseleave == false) {

// Прячем тег <canvas />
canvas.hide();
}
}
}
)
}

// Добавляем тег <area> в тег <map />
areaTag = $('<area />')

// Указываем атрибуты
.attr({

// Ссылка
href: "#",

// Тип области для Opera & Firefox
shape: "poly",

// Координаты контура
coords: coords.stroke
})

// Добавляем в тег <map>
.appendTo(mapTag);

// Очищаем канвас
canvasContext.clearRect(0, 0, imgWidth, imgHeight);

// Цвет обводки
canvasContext.strokeStyle = ((typeof options === 'object') && (typeof options.color === 'string') ? options.color : LibOutLine.Config.OutLine.Color);

// Количество PX для контура
canvasContext.lineWidth = ((typeof options === 'object') && (typeof options.width === 'number') ? options.width : LibOutLine.Config.OutLine.Width);

// Рисуем контур
LibOutLine.Draw(canvasContext, coords.points);

// Заменяем файл на контейнер
$(img).replaceWith(container);

// Элемент не является картинкой, либо ранее уже был высчитан контур
} else {

// Если ранее уже был определен контур
if (typeof $(this).attr('outLineId') === 'number') {

// Контейнер со всеми тегами
container = $(this).parent();

// Тег <canvas />
canvas = container.find('canvas');

// Тег <map /> ( с координатами контура )
mapTag = container.find('map');

// Тег <area />
container.find('area');

// Функция работает только с картинками
} else if ((err === false) && !($(this).is('img'))) {

// Указываем описание ошибки
err = LibOutLine.Config.Error[2];
}
}


// Если указан callBak
if (typeof callBack === 'function') {

// Вызываем callBack и передаем необходимые данные
callBack(err, container, canvas, mapTag, areaTag)
}
}
);

// Возвращаем объект обратно
return this;
};

})(jQuery);


HTML код
<img id="building" src="building.png"/>


Пример использования

$('#building').outline(

/**
* Входящие параметры
*
**/

{
// Цвет контура
color: 'red',
// Ширина контура
width: 1,
// Вывод контура на события
onEvent: {
// Клик по картинке
click: false,
// Курсор НАД картинкой
mouseenter: true,
// Курсор покинул область картинки
mouseleave: false
}
}
,

/**
* callBack после высчитывание контура
*
* @param {[bool]|[string]} err Ошибки
* @param {object} container Курсор на контейнер <span />
* @param {object} canvas Курсор на тег в контейнере <canvas />
* @param {object} mapTag Курсор на тег в контейнере <map />
* @param {object} areaTag Курсор на тег в контейнере <area />
*/

function (err, container, canvas, mapTag, areaTag) {

// Если произошла ошибка
if (err) {

// Выводим в консоль
console.log(err);

// Ошибок не было, можем начать управлять контуром
} else {

// Вешаем слушателей на тег <map> (слушателей можно указать другие, данные лишь для примера)
$(mapTag).on('click mouseenter mouseleave mousemove mousedown mouseup mousewheel', function (e) {

// Мышь над тегом <map />
if (e.type === 'mouseenter') {

// Показываем контур
$(canvas).css('display', 'inline-block');

// Мышь убрали с тега <map />
} else if (e.type === 'mouseleave') {

// Прячем контур
$(canvas).hide();

//
} else {
// ........ Под каждое действие можно создать свой функционал
}
}
)
}
}

);


Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.