Javascript, Jquery: Selectable + Transform scale решение проблемы выделения

Всем привет. Сегодня Я решил рассказать о том как можно заставить работать плагин jquery ui selectable на элементе с CSS стилем transform scale.
Для тех кто не в курсе, проблема заключается в том, что выделение работает не совсем корректно. Связано это с неверным вычислением координат выделяемых объектов (позиции правой и нижней грани). Причиной такого поведения является стиль transform: scale(число не равное единице). Что бы заставить плагин работать корректно можно сделать следующие телодвижения. Сразу хочу оговориться что придется править исходный код jquery ui, поэтому Вам понадобиться не сжатая версия скрипта.
Первым делом заводим глобальную переменную, отвечающую за масштаб контейнера с выделяемыми объектами. Назовем данную переменную zoom.

var zoom = 0.5; //равен CSS значению scale

Далее открываем файл jquery-ui.js и ищем строчку с функцией _mouseDrag в плагине selectable. У меня это была стока 11927, у Вас возможно будет другая. Все зависит от сборки плагина.

_mouseDrag: function(event) {

		this.dragged = true;

		if (this.options.disabled) {
			return;
		}

		var tmp,
			that = this,
			options = this.options,
			x1 = this.opos[0],
			y1 = this.opos[1],
			x2 = event.pageX,
			y2 = event.pageY;

		if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
		if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
//и т.д......

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

_mouseDrag: function(event) {
    var zoomMode = typeof(zoom) !== 'undefined' && parseFloat(zoom) !== 1;
//....

Далее ищем блок кода:

if (options.tolerance === "touch") { 
   hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
} else if (options.tolerance === "fit") {
   hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
}

Вносим следующие правки:

var r = zoomMode ? (selectee.right - selectee.left) * parseFloat(zoom) + selectee.left : selectee.right;
var b = zoomMode ? (selectee.bottom - selectee.top) * parseFloat(zoom) + selectee.top : selectee.bottom;
if (options.tolerance === "touch") { 
   hit = ( !(selectee.left > x2 || r < x1 || selectee.top > y2 || b < y1) );
} else if (options.tolerance === "fit") {
   hit = (selectee.left > x1 && r < x2 && selectee.top > y1 && b < y2);
}

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

Запись опубликована в рубрике JavaScript с метками , , , , , , , , , , , , , . Добавьте в закладки постоянную ссылку.