Вращение объектов в jQuery UI с помощью CSS transform

Вращение объектов в jQuery UI с помощью CSS transform

Всем привет!

Сегодня я хочу вам рассказать о плагине для jQuery UI, который я написал в процессе работы над одним проектом. Сразу скажу, что плагин в своем роде уникальный, таких больше нет (ну или я не нашел).

Собственно, плагин добавляем в jQuery UI возможность вращать предметы вокруг их центра, зажав определенный контрол мышкой. Этот плагин дополняет имеющиеся в jQuery UI возможности изменения размера и перемещения.

Требования

Для работы плагина понадобится:

  • jquery.js
  • jquery.ui.core.js
  • jquery.ui.widget.js
  • jquery.ui.mouse.js
  • jquery.ui.draggable.js

Совместимость

В настоящий момент плагин работает в браузерах, которые поддерживают свойство CSS transform: matrix(...). Это последние сборки Оперы, Фаерфокса, Сафари и Хрома. В ИЕ7 работает, но с глюками, в ИЕ8 не работает.

Реализация

Вращение объектов можно делать за счет свойства CSS transform, у которого есть такие разновидности:

  • transform: matrix(0,70710678, 0,70710678, -0,70710678, 0,70710678, 0, 0) — трансформирует элемент по указанной таблице (здесь поворот на 45 градусов);
  • transform: rotate(45deg) — поворачивает объект на указанный угол (здесь 45).

Нормальный человек решит, для вращения лучше использовать transform: rotate, но я решил по-другому. И тут дело не в моей ненормальности — дело в том, что transform: rotate умеет только вращать, в то время как с помощью transform: matrix можно делать вертикальные и горизонтальные отображения объектов, скручивать их и так далее. Предел для фантазии существенно шире.

В обоих случаях вращение будет происходить по умолчанию относительно центра объекта. Для смены центра есть свойство transform-origin.

CSS transform: matrix

Свойство transform: matrix имеет шесть аргументов:

transform: matrix(a, b, c, d, transX, transY);

Они отвечают за преобразование элемента. Для любителей математики даю картинку:

CSS transform matrix

Если по-простому: точка с координатами (x, y, 1) после преобразования с помощью указанной матрицы перейдет в точку с координатами (a*x + c*y + transX, b*x + d*y + transY, 1). Тут сразу становится понятным, что transX и transY — сдвиги по осям X и Y, соответственно, и в нашем случае они будут по нулям.

После некоторых размышлений можно так же прийти к выводу, что для нормального положения объекта необходимо следующее:

transform: martix(1, 0, 0, 1, 0, 0);

IE: filter, -ms-filter и -ms-transform

Чисто теоретически вращение можно сделать и для Эксплорера начиная с 6-ой версии, но мне пока что удалось сделать только для IE7, да и то с багами.

Используя различные варианты ослиного свойства filter, можно вращать объект. Но вот беда — вращение происходит не относительно центра, и точно подсчитать отрицательные отступы для компенсации сдвига при вращении мне пока что не удалось.

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

Алгоритм работы

Сценарий такой: при нажатии и перетаскивании хендлера скрипт считает угол поворота, синус и косинус и подставляет значения в transform: matrix(cos, sin, -sin, cos, 0, 0).

Для начала нужно сделать HTML для объекта, который мы будем вращать. Опытным путем было установлено, что помимо самого объекта вращения (я делал это все с перетаскиванием и ресайзом) нужно еще два врапера:

<div id="draggable-zone">
<div id="draggable-wrapper" style="width: 150px; height: 150px; left: 225px; top: 175px;">
<div id="resizable-wrapper">
<img src="images/earth.png" width="150" height="150" alt="Планет Земля" id="elem-wrapper" />
</div>
</div>
</div>

Теперь нам нужен хендлер, зажав и перетягивая который мы вращаем объект. Он создается динамически внутри плагина. Сделаем стили:

#draggable-zone {
background: #000 url(images/space.jpg) 0 0 no-repeat;
border: 3px solid #000;

height: 500px;
margin: 2em auto;
overflow: hidden;
width: 600px;}

.ui-wrapper {
overflow: visible !important;}

.ui-resizable-handle {
background: #f5dc58;
border: 1px solid #FFF;

z-index: 2;}

.ui-rotatable-handle {
background: #f5dc58;
border: 1px solid #FFF;
border-radius: 5px;
-moz-border-radius: 5px;
-o-border-radius: 5px;
-webkit-border-radius: 5px;
cursor: pointer;

height: 10px;
left: 50%;
margin: 0 0 0 -5px;
position: absolute;
top: -5px;
width: 10px;}

.ui-rotatable-handle.clone {
visibility: hidden;}

Теперь вычисляем угол. Угол вычисляется на основе координат курсора и центра вращаемого объекта (в радианах):

this.getAngle = function(ms, ctr) {
var x = ms.x - ctr.x,
y = - ms.y + ctr.y,
hyp = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)),
angle = Math.acos(x / hyp);

if (y < 0) {
angle = 2 * Math.PI - angle;
}

return angle;
}

Теперь нужно перевести его в градусы и вычесть 90 градусов (геометрия, все дела):

angle = _this.radToDeg(_this.getAngle(mouse_coords, center_coords)) - 90;

Вот это в целом все сложные моменты. После получения угла нужно вычислить синус и косинус и поставить их в transform: matrix (cos, sin, -sin, cos, 0, 0).

Использование

В случае, если вы используете помимо вращение еще и изменение размера и перетаскивание, задавать их следует вот в такой порядке:

В действии

Вот и все. Смотрим пример и качаем сам скрипт.

Пример Архив со скриптами

P.S.

Если у кого-то из читателей есть идеи или готовые решения как сделать все это работающим в IE7-9, пишите на мыло или в комментариях.

Расскажите друзьям

Оцените статью:
  • 1
  • 2
  • 3
  • 4
  • 5

Комментарии — 45

nikitammf
Матрица трасформации приведена не верна. Вместо a,b,c,d должны быть scaleX*cos(alpha), -scaleY*sin(alpha), scaleX*sin(alpha), scaleY*cos(alpha), соответсвенно. Где scaleX, scaleY - растяжение.сжатие вдоль соответсвующей оси, а alpha - угол поворота.
#
nikitammf  
Scriptin
-ms-transform.htc не может помочь решить проблемы? Там есть функция rotate(iDeg).
Grin
nikitammf, это как бы одно и тоже, просто записано по-другому. Согласен, что ваш вариант записи понятнее в некотором роде.
#
Grin  
Grin
Scriptin, есть где-нибудь описание работы это штуки?
#
Grin  
Pashkou
Вот еще интересный плагин в тему: http://wiki.github.com/heygrady/transform/

Особенно порадовала кнопочка на этой странице http://wiki.github.com/heygrady/transform/demo-2
Виталий
> Сразу скажу, что плагин в своем роде уникальный, таких больше нет (ну или я не нашел).

ну насчет этого вы преувеличили, таких реализаций уже много.
начиная от приведенного Pashkou и заканчивая вот этим http://www.zachstronaut.com/posts/2009/08/07/jquery-animate-css-rotate-scale.html

его я недавно использовал в одном из проектов.
к сожалению тут не предусмотрен ИЕ, хотя даже у вас не получилось сделать чтобы ие нормально вращался четко вокруг центра.


а так же у всех приведенных плагинов проблема с Оперой, которая с 10 версии поддерживает transform ...

чтобы заставить ее работать в выше приведенном плагине достаточно было
добавить вот эту строчку
var properties = [ 'transform', 'WebkitTransform', 'MozTransform', 'OTransform'];
#
Виталий
Grin
Виталий, я не преувеличил. Приведите пример, где можно с помощью мышки (это задача плагина) ресайзить и вращать объект. Все приведенные комментаторами примеры умеют делать заданный поворот, то есть просто изменить угол на заданное значение. Иногда с анимацией. Но, как я уже писал, я не встречал такого, чтобы вращение можно было делать с помощью мышки.
#
Grin  
CTAPbIu_MABP
прикольно вышло, и пример хороший. интересует будеш ли ты делать деформирование фигуры?
Grin
CTAPbIu_MABP, пока что этого не требуется для моего случая. Может быть ради интереса попробую
#
Grin  
Сергей Чикуёнок
-webkit-transform: rotate(45deg) skewX(45deg) scale(0.5);

и можно не возиться с матрицами
Scriptin
Grin: >есть где-нибудь описание работы это штуки?
Это работает как обычный CSS3:
-ms-transform: rotate(45deg)

и т.п. Поддерживаются только rotate и scale, судя по содержимомому.
Grin
Сергей, в моей задаче еще нужно было делать зеркальное отображение предметов.

Scriptin, спасибо
#
Grin  
Сергей Чикуёнок
в моей задаче еще нужно было делать зеркальное отображение предметов.
-webkit-transform: scale(-1, 1);

Всё, что можно делать матрицей, можно делать упрощёнными функциями.

А для IE есть Matrix Filter
Grin
Сергей, в общем в моей ситуации было проще и удобнее сделать матрицей.
Matrix Filter имеет ряд существенных отличий от простого transform. То есть на статике (просто повернуть картинку без последующих растягиваний) все отлично, но если после этого еще изменять размеры картинки, то начинается веселье.
#
Grin  
Владимир
Неплохая штука. В примере еще бы посоветовал отделить контрол вращения от контролов масштабирования, т.е. чтобы те не вращались вместе с объектом.
#
Владимир  
versoul
заметил баг,если развернуть фигуру на бок,то ресайзится не корректно
#
versoul
Radik
А чего не указали название мат метода? Если мне память не изменяет то это Афинные преобразования.
#
Radik
Вова
Классная штука очень понравилась)
Никита
Отлично выполненная работа, чесно скажу авторам глубокая похвала.
Васек
Вижу идут споры по поводу уникальности, сколько не бродил по просторам интернета, такого я не видел, скажу точно это уникальный плагин.
Deymos1986
Спасибо большое, интересно будет особенно для интернет магазинов. Чтобы лучше показать товар, спасибо большущее!
LicH
Привет, а как насчёт мнимого 3D?
Т.е. чтоб объект вращался не вокруг z-оси как сейчас а, например, вокруг своей x- или y-оси?
Вроде, далеко ходить не надо или всё-таки сложно?

В этом примере есть skrew:
http://wiki.github.com/heygrady/transform/demo-2
Но это всё 2D, как я понял.
#
LicH
Бродяга
А у меня что-то заглючило, не пошло, попробовал браузер обновить но все равно, у меня моззила.
Радиофизик
Оу, спасибо! Только как всегда эксплорер портит картину)
Alt-Ctrl-Zet
Недавно только начал осваивать азы, так что голова чуть не лопнула пока все осознал)
intero
пора изучать js. может у кого есть сканы хороших учебников по js? с удовольствием приму в дар )))
Левитра
Спасибо за интересный пример, в данный момент как раз осваиваюсь с jquery
Diamaxx
на фриаланс-сайтах не раз видел объявления - сделать вращение объектов в 2д, 3д или их симуляцию. Мог бы заработать на этом).
Xstroy
Жаль придётся ещё немного подождать, пока сменится модельный ряд браузеров.
Вадим
Замечательный скрипт!

А можете-ли подсказать как контролы сделать видимыми при расположении картинки под другой?
#
Вадим  
Марина
Diamaxx я тоже обращал внимание на эти просьбы =)) и в правду мог заработать авор =)
Ольга
Спасибо за полезную информацию,ещё обязательно вернусь и не раз
skforussia
спасибо за обзор давно уже подобный эффект хотел сделать
Anvil
Как начал пользоваться jquery, жизнь без нее не представляю.
#
Anvil
Sandrik
Недавно делал простой drag n drop по другой статье, где некоторые утверждают, что это самый элегантный и умный способ. Но тем не менее, этот пример тормозит меньше, а функций больше. Спасибо! Но у меня только вопрос: как добавить на страницу множество разных объектов, чтобы не прописывать для каждого свой скрипт и не называть блоки разными id? Т.е. чуть-чуть автоматизировать работу сайта, где такие объекты будут добавляться часто. Примерно как сейчас у меня на домашней странице (sandrik.co.cc), только с использованием этого плагина :)
Сергей
Если ли возможность показывать хендлер вращения только при наведении на объект?
Аналогично функционалу в ui.resizable `autoHide`.
#
Сергей
Сергей
Да. Достаточно добавить в стили:
.ui-resizable-autohide .ui-rotatable-handle{display:none;}
#
Сергей
Димитриус
До жопы ваш скрипт, если он не работает во всех браузерах.
#
Димитриус
Илья
Скрипт очень понравился! Маленький вопрос: к примеру у меня 2 блока, один пустой, а в другом различные фигуры... Так вот я их перетаскиваю в различные положения, разворачиваю и т.д. И как это все можно сохранить на сайте? Чтоб обнавляешь страницу - а оно все на своих местах??? Если кто знает ответ - отпишите на iluffka999@gmail.com/ Очень нужно!!!
#
Илья
Анастасия
тоже не во всех браузера работает(((печально(((
Артур
А как насчет если я хочу по завершении вращения дернуть свой callback? Почему б не организовать в виде полноценного UI-виджета?
#
Артур  
aal
Возможно ли как-то сохранить параметры, которые содержат текущее состояние картинки (координаты, угол поворота) для того, чтобы после эти параметры можно было использовать для воссоздания копии результата действий посетителя сайта?
#
aal
Trev
Even once translated in English I got the gist of it! Thanks for the script!
Light
Hi,
Even though the plugin was created almost 3 years ago, it is still unique - I couldn't find a way to support both jQueryUI scaling and rotation at the same time.
However, because it is old, it is not working with the recent versions of jQuery and jQueryUI.
Do you have an update so it will work with the latest versions?

Thank you,
#
Light

Новый комментарий

как выглядит какой тег
жирный текст <b>жирный текст</b>
курсивный тект <i>курсивный тект</i>
зачеркнутый текст <s>зачеркнутый текст</s>
подчеркнутый текст <u>подчеркнутый текст</u>
ссылка <a href="адрес">ссылка</a>
function foo() { ... }
<pre><code>function foo() { ... } </code></pre>
разрешенные теги или посмотреть как будет выглядеть
как выглядит какой тег
жирный текст <b>жирный текст</b>
курсивный тект <i>курсивный тект</i>
зачеркнутый текст <s>зачеркнутый текст</s>
подчеркнутый текст <u>подчеркнутый текст</u>
ссылка <a href="адрес">ссылка</a>
function foo() { ... }
<pre><code>function foo() { ... } </code></pre>
разрешенные теги или посмотреть как будет выглядеть

metin2 pvp metin2 pvp serverler pvp serverler