Драг на JavaScript
;
О чем это я?
Иногда разработчик сталкивается с необходимостью разрешить пользователю свободно перемещать некоторые объекты на странице. Например, такая ситуация возникает при программировании некоторых JavaScript-игр. В этой статье изложена общая теория программирования драга на JavaScript.
Словарь чисто технических терминов ;)
хомяк - мышь
белый кролик - курсор хомяка
драг (тяг) объекта - перетаскивание объекта белым кроликом
Принципы управления драгом.
Сам по себе, объект, над которым совершается попытка драга, не будет перемещаться. Но мы заставим его идти за белым кроликом :) И не таких заставляли ;)
Сделать это можно, задав обработчики "тягучих" (dragging) событий: ondragstart, ondrag, ondragend - для перемещаемого объекта. Но. Мы пойдем другим путем. Поясню почему. При драге объекта <img> на событии ondrag белый кролик приобретает форму перечеркнутой окружности, которая (форма) всем видом своим намекает на то, что объект перетягивать нельзя. И избавиться от этого побочного эффекта мне не удалось, как я ни хитрил ;) Кроме того, drag-события поддерживаются не всеми версиями современных браузеров (например, Opera6 - не поддерживает). Поэтому будем применять менее специализированные обработчики событий onmousedown, onmousemove и onmouseup. Отмечу, что при использовании этих событий можно установить произвольную форму белого кролика.
Разберем названные события:
onmousedown - возникает при нажатии на любую кнопку хомяка. Внутри обработчика event.button указывает, какая кнопка нажата: 1 = левая, 2 = правая.onmousemove - возникает при перемещении белого кролика по экрану. Внутри обработчика event.x и event.y - текущие координаты "горячей" точки белого кролика на экране.
onmouseup - возникает при отпускании любой из нажатых кнопок хомяка. Определение, какая кнопка отпущена - как и для onmousedown выше.
Кроме указанных, нам потребуется изменить (точнее, отключить) стандартный обработчик события oncontextmenu, которое возникает при нажатии правой кнопки хомяка и приводит к выводу на экран контекстного меню. Образно выражаясь, эта блокировка - т.н. защита "от дурака", возжелавшего в самый разгар перетаскивания вызвать контекстное меню (при этом произошла бы потеря управления над процессом тяга).
Обратимся к техническим деталям реализации драга для объекта img.
Во-первых, нужно установить стилевой параметр position в значение absolute (чтобы не привязываться ни к каким относительным координатам охватывающего объекта) и задать начальные координаты. Во-вторых, нужно установить указанные обработчики событий. Так что, определение перетаскиваемой картинки может быть таким (для некоторого файла star.gif):
<img src="star.gif" style="position: absolute; left: 100px; top: 100px;" onmousedown="mousedown(this);" onmousemove="mousemove(this); return false;" onmouseup="mouseup(this);" oncontextmenu="return false;" >
Как видно, обработчик oncontextmenu просто возвращает значение false, что приводит к "обрыву" цепочки обработчиков этого события. Т.е. мы указываем, что событие целиком и полностью отработано нами и дальнейшей отработке не подлежит. То же верно и для mousemove (при отладке выяснилось, что нужно поставить "обрыв").
Рассмотрим, что должны делать обработчики хомячных событий для реализации перетаскивания в наиболее общем случае.
<script language="JavaScript"> /* Сервисная функция. Возвращает значение свойства pr объекта obj в формате целого числа. */ function getPr( obj, pr) { return parseInt(obj[pr],10); } /* Переменные для фиксирования координат "горячей" точки белого кролика и состояния левой кнопки хомячка. */ var x, y, mouse_state = 0; function mousedown( obj) { if (event.button==1) { /* проверяем нажатую кнопку */ mouse_state = 1; /* левая кнопка нажата */ /* сохраняем текущие координаты "горячей" точки белого кролика */ x = event.x; y = event.y; } } function mousemove( obj) { if (mouse_state) { /* проверяем, нажата ли кнопка */ /* производим перемещение объекта */ obj.style.left = getPr(obj.style,'left')+(event.x-x); obj.style.top = getPr(obj.style,'top')+(event.y-y); /* сохраняем текущие координаты "горячей" точки белого кролика */ x = event.x; y = event.y; } } function mouseup( obj) { if ((event.button==1)&&(mouse_state)) /* проверяем отжатую кнопку */ mouse_state = 0; /* левая кнопка не нажата */ } </script>