2bf008d1d322ed159cce822c24da6859.ppt
- Количество слайдов: 23
В DRAG AND DROP JAVA SCRIPT Ст. преподаватель: Летвинко. П. С. Костанайский государственный университет имени А. Байтурсынова
Основная логика drag and drop Организовать перенос элементов по странице довольно просто. Для этого нужно: При помощи события mouse. Down отследить клик на переносимом элементе При каждом движении мыши в обработчике события mouse. Move передвигать переносимый элемент по странице. При отпускании кнопки мыши, то есть наступлении события mouse. Up - остановить перенос элемента и произвести все действия, связанные с окончанием drag and drop.
Координаты и кнопка мыши При обработке событий, связанных с мышью, нужен кроссбраузерный способ получения координат курсора из события в обработчике. Кроме того, необходимо знать нажатую кнопку мыши. Для этого будем использовать свойства which и page. X/page. Y, полное описание и механизмы кроссбраузерной реализации которых есть в статье. which кнопка мыши - 1: левая, 2: средняя, 3: правая page. X/page. Y координаты курсора относительно верхнего-левого угла документа (с учетом прокрутки)
Демо На демке ниже обработчик mouse. Move отслеживает координаты курсора мыши относительно левого-верхнего угла страницы, используя кроссбраузерную обертку fix. Event. document. onmousemove = mouse. Move function mouse. Move(event){ event = fix. Event(event) document. get. Element. By. Id('mouse. X'). value = event. page. X document. get. Element. By. Id('mouse. Y'). value = event. page. Y } Координата X: Координата Y:
Drag and drop контроллер Перед дальнейшим развитием проведем реорганизацию кода. Используем способ описания объекта без new (описан здесь как фабрика объектов), чтобы объявить объект drag. Master, предоставляющий необходимый функционал и отслеживающий перенос.
Визуальное перемещение элемента Для того, чтобы перенести элемент, ему нужно поставить значение CSS-свойства position в absolute. Тогда он будет позиционироваться относительно верхнего-левого угла документа (точнее говоря, относительно ближайшего родителя, у которого position - relative/absolute, но у нас таких нет), и установка CSS-свойств left и top в координаты курсора мыши поместит левый-верхний угол элемента непосредственно под указатель.
Начало движения: сохранение позиции курсора в элементе Посетитель обычно кликает не в левый-верхний угол, а куда угодно на элементе. Поэтому чтобы элемент не прилипал к курсору верхним-левым углом, к позиции элемента необходимо добавить смещение мыши на момент клика. На рисунке ниже mouse. X/mouse. Y - координаты курсора мыши, а position. X/position. Y координаты верхнего-левого угла элемента, которые легко получить из DOM:
Опускаем элемент Когда иконка опущена, нам необходимо определить, куда. В другое хранилище - переместить. В корзину удалить, и т. п. Существенная техническая проблема заключается в том, что событие mouseup сработает не на корзине, а на переносимом элементе, т. к. курсор мыши находится именно над ним. Поэтому в событии будет информация об элементе непосредственно под курсором. На картинке выше event. target = сердечко, а корзина в объекте события event не присутствует. Определить, что иконка опущена на корзину, можно, сравнив координаты корзины с коорданатами мыши на момент события.
Индикация переноса над объектом В удобном интерфейсе мы, скорее всего, захотим как-то показывать посетителю, над каким объектом он сейчас находится. Единственно место, где это можно сделать - обработчик mouse. Move. Сама проверка, над чем курсор сейчас находится, полностью аналогична mouse. Up. Однако, так как mouse. Move выполняется при каждом передвижении мыши, его надо максимально оптимизировать. Функция get. Position - довольно медленная: она работает с DOM, и ей надо пройти по всей цепочке offset. Parent. Выполнять ее каждый раз при движении мыши для поиска текущего акцептора - все равно что нажать на большой-большой тормоз. Стандартным выходом в такой ситуации является кеширование координат акцепторов, а точнее - их ограничивающих прямоугольников, так чтобы код mouse. Move был максимально прост.
Отделяем начало drag'n'drop от простого клика Следуя общему принципу отделения мух от котлет лучше отделить простой клик на объекте от начала drag and drop. Еще одна причина - дорогая инициализация drag & drop: нужно прокешировать все возможные акцепторы. Совершенно не обязательно это делать на mousedown, если имеем простой клик. Как отделить? Очень просто: При mousedown запомнить координаты и объект, но пока не начинать перенос Если произошло событие mouseup - это был всего лишь клик, сбросить координаты В mousemove проверить: если есть запомненные координаты и курсор отошел от них хотя бы на 2 пикселя - начать перенос
Оптимизация Бывает, что возможных акцепторов очень много. Тогда код, кеширующий прямоугольники при начале переноса, будет тормозить. Визуально это проявляется как задержка от клика на объекте до его фактического переноса потому что долго, с 100% поеданием одного ядра CPU обрабатывается mousedown. Речь тут идет о 100 акцепторах или больше например, при переносе между длинными списками или деревьями. Хотя какие-то тормоза могут быть заметны и от 50. Принципиальных решений здесь два.
Смена способа вычисления координат Этот малоизвестный метод работает во всех браузерах и возвращает элемент по координатам на странице. Firefox/IE используют для этого client. X/Y, а Opera, Chrome и Safari - page. X/Y. Возвращенный элемент является самым глубоко вложенным на точке с координатами (x, y). Это может быть текстовый узел в том числе. Следуя по цепочке родителей, легко найти нужного акцептора. На время вызова element. From. Point необходимо спрятать переносимый элемент, чтобы он не закрывал акцептора.
Убрать элемент из-под курсора В ряде задач допустимо не сохранять переносимый элемент под курсором, а передвинуть его на несколько пикселей в сторону, то mouse. Up и mouse. Move будут срабатывать уже на акцепторе, который станет возможным получить из event. target. Да, получится не так красиво, но это может быть единственным выходом.
Упростить объект переноса Можно переносить не сам объект, а его "аватар", схематическое изображение. Это может быть полезно: для ускорения отрисовки например, при переносе сложного блока можно переносить такого же размера серый прямоугольник чтобы убрать элемент из-под курсора переносить сам объект в стороне от мыши может быть некрасиво. А обозначить перенос небольшой аватаркой рядом с курсором - уже другое дело.
Рефакторинг Код drag. Master'а на текущий момент сочетает весь функционал по отслеживанию переноса, отображению и опусканию на акцептор. Целесообразно его разделить на три компоненты: переносимые объекты, цели переноса (акцепторы) и менеджер, который следит, что куда переносится.
Перемещения акцепторов в процессе переноса В процессе переноса акцепторы объекты могут сдвигаться, освобождая место. Если вы используете кеш координат акцепторов, то при этом производится соответствующее обновление кеша, но вместо полного перевычисления обновляются только координаты сдвинувшихся объектов. Например, при раздвижении списка вниз увеличиваются Y-координаты всех сдвинувшихся LI. При этом для удобного обновления кеш делается не массивом, а объектом с доступом по ID элемента, так чтобы можно было легко обновить именно нужные координаты.
Drag and drop "между". Иногда, например, при смене позиции элемента в списке, объект переносится не на акцептор, а между акцепторами. Как правило, "между" имеется в виду по высоте. Для этого логику определения current. Drop. Target нужно поменять. Возможно два варианта: Допустим перенос как между, так и над В этом случае акцептор делится на 3 части по высоте client. Height: 25% - 50% - 25%, и определяется попадание координаты события на нужную часть. Перенос только между Акцептор делится на две части: 50% - 50%
Анимация отмены переноса Обычно при переносе объекта куда-либо посетитель может просто отпустить его в любом месте. При этом drag and drop фреймворк анимирует отмену переноса. Один из частых вариантов скольжение объекта обратно к исходному месту, откуда его взяли. Конечно, для этого исходное место необходимо запомнить. Если перетаскивается аватарка(клон), то можно его и просто уничтожить.
Проверка прав Совершенно не факт, что любой объект можно перенести на любой аксептор. Как правило, все с точностью наоборот. Акцептор может быть недоступен по двум причинам: либо это несовпадение типов, для этого drag and drop должен предусматривать различные типы объектов/акцепторов и проверку их соответствия либо у посетителя недостаточно для этого прав, в рамках наложенных CMS ограничений При переносе над недоступным акцептором get. Current. Target просто возвращает null.
Иногда проверку прав и результата переноса необходимо делать на сервере. Как правило, такую проверку выполняют только при mouse. Up, чтобы не нагружать сервер излишними запросами во время mouse. Move. Здесь используется два подхода
Синхронный Xml. Http. Request Запрос отправляется синхронно, чтобы не нарушать общий поток выполнения. Все хорошо, вот только браузер видимо подвисает при отпускании кнопки мыши. Да и другие недостатки у синхронного запроса есть. Отложенная отмена переноса Более продвинутый и удобный вариант. Основан на "оптимистичном" сценарии, по которому перенос, как правило, проходит успешно.
Резюме Вы узнали основные принципы и особенности реализации drag and drop в яваскрипт. Рассмотрели ряд оптимизаций и различные организации переноса на уровне UI. Все современные javascript-библиотеки используют описанную схему и какие-то (не все) из оптимизаций.
конец


