Подтвердить что ты не робот

Перетаскивание и изменение размера CSS-преобразованных элементов

Если, например, мы устанавливаем атрибут -vendor-transform: rotate(40deg) css на прямоугольник <div>, все внезапное перетаскивание и изменение размера становятся очень странными и ошибочными.

Вот пример с простым jQueryUI: http://jsfiddle.net/Ja4dY/1/

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

Ну, конечно, "проблема" в том, что направление элемента изменится. Таким образом, левый может быть прав, верхнее - нижнее и что-то среднее между ними, а код Javascript по-прежнему обрабатывает каждое направление, поскольку оно не будет преобразовано.

Итак, вопрос: Как мы можем компенсировать преобразованные/повернутые элементы?

Любые хорошие ресурсы/книги/блоги также очень приветствуются.

4b9b3361

Ответ 1

Вы можете получить текущую матрицу преобразования, которая применяется к элементу, используя getComputedStyle(). Вы можете использовать это, чтобы преобразовать текущую позицию мыши в ее положение в преобразованном пространстве и посмотреть, находятся ли события щелчка/перетаскивания внутри границы элемента и/или углов. Хорошие ресурсы для этого:

http://www.useragentman.com/blog/2011/01/07/css3-matrix-transform-for-the-mathematically-challenged/

http://www.eleqtriq.com/2010/05/css-3d-matrix-transformations/

Кстати, как вы переживаете, это нетривиально для кода. Мы должны были сделать это для Sencha Animator, и это было зверь.

Ответ 2

Проблема в том, что функции, которые заставляют элементы перетаскивать, используя jQuery UI или нет, в значительной степени зависят от встроенной функции getBoundingClientRect() для определения положения элемента и т.д.

При применении преобразований CSS3, таких как вращение, значения getBoundingClientRect() или равноценной функции jQuery offset(), используемые в jQuery UI, больше не работают так, как ожидалось, и позиция указателя мыши становится беспорядочной, поскольку размер элемент внезапно ошибочен после его поворота.

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

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

function monkeyPatch_mouseStart() {
     var oldFn = $.ui.draggable.prototype._mouseStart ;
     $.ui.draggable.prototype._mouseStart = function(event) {

            var o = this.options;

           function getViewOffset(node) {
              var x = 0, y = 0, win = node.ownerDocument.defaultView || window;
              if (node) addOffset(node);
              return { left: x, top: y };

              function getStyle(node) {
                return node.currentStyle || // IE
                       win.getComputedStyle(node, '');
              }

              function addOffset(node) {
                var p = node.offsetParent, style, X, Y;
                x += parseInt(node.offsetLeft, 10) || 0;
                y += parseInt(node.offsetTop, 10) || 0;

                if (p) {
                  x -= parseInt(p.scrollLeft, 10) || 0;
                  y -= parseInt(p.scrollTop, 10) || 0;

                  if (p.nodeType == 1) {
                    var parentStyle = getStyle(p)
                      , localName   = p.localName
                      , parent      = node.parentNode;
                    if (parentStyle.position != 'static') {
                      x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
                      y += parseInt(parentStyle.borderTopWidth, 10) || 0;

                      if (localName == 'TABLE') {
                        x += parseInt(parentStyle.paddingLeft, 10) || 0;
                        y += parseInt(parentStyle.paddingTop, 10) || 0;
                      }
                      else if (localName == 'BODY') {
                        style = getStyle(node);
                        x += parseInt(style.marginLeft, 10) || 0;
                        y += parseInt(style.marginTop, 10) || 0;
                      }
                    }
                    else if (localName == 'BODY') {
                      x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
                      y += parseInt(parentStyle.borderTopWidth, 10) || 0;
                    }

                    while (p != parent) {
                      x -= parseInt(parent.scrollLeft, 10) || 0;
                      y -= parseInt(parent.scrollTop, 10) || 0;
                      parent = parent.parentNode;
                    }
                    addOffset(p);
                  }
                }
                else {
                  if (node.localName == 'BODY') {
                    style = getStyle(node);
                    x += parseInt(style.borderLeftWidth, 10) || 0;
                    y += parseInt(style.borderTopWidth, 10) || 0;

                    var htmlStyle = getStyle(node.parentNode);
                    x -= parseInt(htmlStyle.paddingLeft, 10) || 0;
                    y -= parseInt(htmlStyle.paddingTop, 10) || 0;
                  }

                  if ((X = node.scrollLeft)) x += parseInt(X, 10) || 0;
                  if ((Y = node.scrollTop))  y += parseInt(Y, 10) || 0;
                }
              }
            }

                this.helper = this._createHelper(event);
                this._cacheHelperProportions();

                if($.ui.ddmanager)
                    $.ui.ddmanager.current = this;

                this._cacheMargins();

                this.cssPosition = this.helper.css("position");
                this.scrollParent = this.helper.scrollParent();

            this.offset = this.positionAbs = getViewOffset(this.element[0]);
                this.offset = {
                    top: this.offset.top - this.margins.top,
                    left: this.offset.left - this.margins.left
                };

                $.extend(this.offset, {
                    click: {
                        left: event.pageX - this.offset.left,
                        top: event.pageY - this.offset.top
                    },
                    parent: this._getParentOffset(),
                    relative: this._getRelativeOffset()
                });

                this.originalPosition = this.position = this._generatePosition(event);
                this.originalPageX = event.pageX;
                this.originalPageY = event.pageY;

                (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));

                if(o.containment)
                    this._setContainment();

                if(this._trigger("start", event) === false) {
                    this._clear();
                    return false;
                }

                this._cacheHelperProportions();

                if ($.ui.ddmanager && !o.dropBehaviour)
                    $.ui.ddmanager.prepareOffsets(this, event);

                this.helper.addClass("ui-draggable-dragging");
                this._mouseDrag(event, true);

                if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
                return true;
     };
 }
monkeyPatch_mouseStart();

И вот FIDDLE, показывающий, что он работает как ожидалось с jQuery UI, перетаскиваемым и изменяемым по размеру!

Ответ 3

Я нашел это... Это рабочий пример плюс информация, демонстрация и ссылка для скачивания.

jquery-ui-rotation-using-css-transformlive-demo

Он использует свои собственные библиотеки, но если вы заинтересованы в предмете, вы можете прочитать и узнать, как он его получает.

приветствия и удачи.

Gmo.-

Btw, сеть на русском языке, но с google translate вы можете управлять; -)

Ответ 4

Это не ошибка в jQuery. Просто он не поддерживается. Если вы проверите исходный код jQuery UI, вы поймете, что он не использует матрицу преобразования для вычисления разницы между преобразованным объектом и страницей.

В вашем примере и, вероятно, каждая реализация jQ UI drag пострадает от этой проблемы из-за двух методов в исходном коде JQ UI (около 314 строк файла jquery.ui.draggable.js v1.8.23). Вычисленное смещение не имеет значения об изменении смещения, так как вращение выполняется над центром элемента.

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

Проверьте образец здесь http://jsfiddle.net/mjaric/9Nqrh/

Игнорировать часть с двумя первыми поворотами, они просто сделаны для минимизации строк кода. Третье включает перевод системы координат для расчетной разности. Он будет смещать левый и верхний после выполнения перевода (обратите внимание, что он первый в фильтре).

Если вы хотите избежать первых двух фильтров вращения, вы можете сделать код, используя формулу для двумерного вращения:

x '= x cos f - y sin f

y '= y cos f + x sin f

где f - угол поворота, но это не так просто и также содержит больше строк кода, где вам нужно рассчитать, что такое диагональный угол исходного ограничивающего прямоугольника, так как вам нужен начальный угол верхнего левого угла, в котором координаты x и y по сравнению с осью х (положительная часть). Затем вычислите изменение в x-x 'и y-y'. Но я предсказываю некоторые проблемы со знаком изменений, а кодирование/отладка займет больше времени, чем у меня сейчас. Извините, но я уверен, что вы можете понять, что делать после прочтения этого сообщения.

Ответ 5

Это выглядит лучше, если мы переопределим cursorAt:

$("#foo").mousedown(function (e) { 
    var x = e.pageX - this.offsetLeft;
    var y = e.pageY - this.offsetTop;
    console.log(x);
    $("#foo").draggable("option", "cursorAt", {left: x, top:y});
});

Обновлен скрипт: http://jsfiddle.net/johnkoer/Ja4dY/8/

Ответ 6

Вы сказали, что вас не интересуют решения JQuery, тогда

  • Одно из решений:

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

  • Другое решение:

    Вы можете использовать библиотеки, такие как Raphael JS, для создания объектов для преобразования, перетащить и изменить размер. Raphael JS использует svg!

    Для получения дополнительной информации о Raphael JS

  • Еще одно решение:

    Если вы не хотите использовать библиотеку, такую ​​как Raphael JS, вы можете напрямую использовать SVG с JQuery

    Дополнительные сведения о SVG

Теперь я не могу написать более подробную информацию, я буду расширять это решение завтра.

Надеюсь, что сейчас это поможет.

Ответ 7

Это, действительно, похоже на ошибку в jQuery. Легким обходным решением было бы: объединить изменяемый размер div с контейнером div. Установите .draggable() на внешние div и .resizable() на внутренний div. Это, похоже, отлично работает в Chromium, работающем на Ubuntu. Смотрите Fiddle.

Я покрасил внешний div, чтобы дать вам представление о том, что происходит под капотом.