Перетаскивание, ротация и изменение формы с помощью Raphael JS - PullRequest
4 голосов
/ 14 ноября 2011

Я использую RaphaelJS 2.0 для создания нескольких фигур в div.Каждая фигура должна быть в состоянии перетащить в пределах границ div, независимо.При двойном щелчке по фигуре эта форма должна повернуться на 90 градусов.Затем его можно перетащить и снова повернуть.

Я загрузил некоторый код в фиддлер: http://jsfiddle.net/QRZMS/. В основном это так:

    window.onload = function () {

        var angle = 0;

        var R = Raphael("paper", "100%", "100%"),
            shape1 = R.rect(100, 100, 100, 50).attr({ fill: "red", stroke: "none" }),
            shape2 = R.rect(200, 200, 100, 50).attr({ fill: "green", stroke: "none" }),
            shape3 = R.rect(300, 300, 100, 50).attr({ fill: "blue", stroke: "none" }),
            shape4 = R.rect(400, 400, 100, 50).attr({ fill: "black", stroke: "none" });
        var start = function () {
            this.ox = this.attr("x");
            this.oy = this.attr("y");
        },
        move = function (dx, dy) {
            this.attr({ x: this.ox + dx, y: this.oy + dy });
        },
        up = function () {

        };
        R.set(shape1, shape2, shape3, shape4).drag(move, start, up).dblclick(function(){
            angle -= 90;
            shape1.stop().animate({ transform: "r" + angle }, 1000, "<>");
        });


    }

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

  1. Как я могу автоматически прикрепить вращение к каждой фигуре без необходимости жестко кодировать каждую ссылку на элемент в методе поворота?Т.е. я просто хочу нарисовать фигуры один раз, а затем автоматически выставить их одинаковое поведение, чтобы каждый из них можно было перетаскивать / опускать / поворачивать независимо, без необходимости явно применять это поведение к каждой фигуре.

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

Большое спасибо за любые указатели!

Ответы [ 6 ]

13 голосов
/ 19 ноября 2011

Я несколько раз пытался обернуть голову вокруг нового двигателя трансформации, но безрезультатно.Итак, я вернулся к первым принципам.

Мне наконец-то удалось правильно перетащить объект, который подвергся нескольким преобразованиям, после попытки выяснить влияние различных преобразований - t, T, ... t, ... T, r, R и т.д ...

Итак, вот суть решения

var ox = 0;
var oy = 0;

function drag_start(e) 
{
};


function drag_move(dx, dy, posx, posy) 
{


   r1.attr({fill: "#fa0"});

   //
   // Here's the interesting part, apply an absolute transform 
   // with the dx,dy coordinates minus the previous value for dx and dy
   //
   r1.attr({
    transform: "...T" + (dx - ox) + "," + (dy - oy)
   });

   //
   // store the previous versions of dx,dy for use in the next move call.
   //
   ox = dx;
   oy = dy;
}


function drag_up(e) 
{
   // nothing here
}

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

Вот вам скрипка , с которой вы можете поиграть.1013 * ... и это является рабочим решением для первоначального вопроса.

4 голосов
/ 04 декабря 2011

Я решил проблему перетаскивания, повторно применив все преобразования при изменении значения. Я создал для него плагин.

https://github.com/ElbertF/Raphael.FreeTransform

Демо здесь:

http://alias.io/raphael/free_transform/

3 голосов
/ 15 ноября 2011

Как предполагает Амадан, обычно хорошей идеей является создание функций, когда несколько вещей имеют одинаковые (начальные) атрибуты / свойства.Это действительно ответ на ваш первый вопрос.Что касается второго вопроса, это немного сложнее.

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

  • Создайте пользовательский атрибут для хранения текущего состояния вращения
  • В зависимости от этого атрибута вы решаете, как обрабатывать перемещение.

При условии, что вы будете поворачивать фигуры только на 90 градусов (если это не станет намного сложнее), вы можете определить, как следует манипулировать координатами.

var R = Raphael("paper", "100%", "100%");

//create the custom attribute which will hold the current rotation of the object {0,1,2,3}
R.customAttributes.rotPos = function (num) {
    this.node.rotPos = num;
};

var shape1 = insert_rect(R, 100, 100, 100, 50, { fill: "red", stroke: "none" });
var shape2 = insert_rect(R, 200, 200, 100, 50, { fill: "green", stroke: "none" });
var shape3 = insert_rect(R, 300, 300, 100, 50, { fill: "blue", stroke: "none" });
var shape4 = insert_rect(R, 400, 400, 100, 50, { fill: "black", stroke: "none" });

//Generic insert rectangle function
function insert_rect(paper,x,y, w, h, attr) {
    var angle = 0;
    var rect = paper.rect(x, y, w, h);  
    rect.attr(attr);

    //on createion of the object set the rotation position to be 0
    rect.attr({rotPos: 0});

    rect.drag(drag_move(), drag_start, drag_up);

    //Each time you dbl click the shape, it gets rotated. So increment its rotated state (looping round 4)
    rect.dblclick(function(){
        var pos = this.attr("rotPos");
        (pos++)%4;
        this.attr({rotPos: pos});
        angle -= 90;
        rect.stop().animate({transform: "r" + angle}, 1000, "<>");
    });

    return rect;  
}

//ELEMENT/SET Dragger functions.
function drag_start(e) {
    this.ox = this.attr("x");
    this.oy = this.attr("y");
};


//Now here is the complicated bit
function drag_move() {
    return function(dx, dy) {

        //default position, treat drag and drop as normal
        if (this.attr("rotPos") == 0) {
          this.attr({x: this.ox + dx, y: this.oy + dy});
        }
        //The shape has now been rotated -90
        else if (this.attr("rotPos") == 1) {

            this.attr({x:this.ox-dy, y:this.oy + dx});
        }           
        else if (this.attr("rotPos") == 2) {
            this.attr({x: this.ox - dx, y: this.oy - dy});
        }
        else if (this.attr("rotPos") == 3) {
             this.attr({x:this.ox+dy, y:this.oy - dx});

        }      
    }
};

function drag_up(e) {
}

Я не могу придумать ясного краткого способа объяснить, как работает drag_move.Я думаю, что лучше всего посмотреть код и посмотреть, как он работает.По сути, вам просто нужно понять, как переменные x и y теперь обрабатываются из этого нового повернутого состояния.Без меня, рисующего много графики, я не уверен, что смог бы быть достаточно ясным.(Я много поворачивал голову в сторону, чтобы понять, что делать).

У этого метода есть несколько недостатков:

  • Он работает только на 90 градусовповороты (для выполнения 45 градусов потребовалось бы больше вычислений, не обращая внимания на какой-либо заданный градус)
  • При начале перетаскивания после поворота наблюдается небольшое движение.Это связано с тем, что перетаскивание принимает старые значения x и y, которые были повернуты.Это не большая проблема для такого размера фигуры, но большие фигуры вы действительно начнете замечать, как фигуры прыгают по холсту.

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

Это не полное решение, но онодолжно определенно заставить вас идти по правильному пути.Мне было бы интересно увидеть полную рабочую версию.

Я также создал эту версию на jsfiddle, которую вы можете посмотреть здесь: http://jsfiddle.net/QRZMS/3/

Удачи.

1 голос
/ 04 мая 2012

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

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

Мы можем видеть этот пример с вращением прямоугольника в грохоте и перемещении.

HTML

<div id="foo">

</div>

JAVASCRIPT

var paper = Raphael(40, 40, 400, 400); 
var c = paper.rect(40, 40, 40, 40).attr({ 
    fill: "#CC9910", 
    stroke: "none", 
    cursor: "move" 
}); 
c.transform("t0,0r45t0,0");

var start = function () { 
    this.ox = this.type == "rect" ? this.attr("x") : this.attr("cx");
    this.oy = this.type == "rect" ? this.attr("y") : this.attr("cy");
}, 

move = function (dx, dy) { 
    var r = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
    var ang = Math.atan2(dy,dx);
    ang = ang - Math.PI/4;
    dx = r * Math.cos(ang);
    dy = r * Math.sin(ang);

    var att = this.type == "rect" ? { x: this.ox + dx, y: this.oy + dy} : { cx: this.ox + dx, cy: this.oy + dy };
    this.attr(att);
}, 

up = function () { 
}; 

c.drag(move, start, up);?

DEMO
http://jsfiddle.net/Ef83k/74/

1 голос
/ 14 ноября 2011

Я обычно создаю объект для своей фигуры и записываю обработку события в объект.

function shape(x, y, width, height, a)
{
  var that = this;
  that.angle = 0;
  that.rect = R.rect(x, y, width, height).attr(a);

  that.rect.dblclick(function() {
      that.angle -= 90;
      that.rect.stop().animate({
          transform: "r" + that.angle }, 1000, "<>");
  });
  return that;
}

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

Стоит отметить, что ссылка на объект хранится в «этом». Это связано с тем, что ссылка «это» изменяется в зависимости от области применения. В функции dblClick мне нужно сослаться на значения rect и angle из моего объекта, поэтому я использую сохраненную ссылку that.rect и , которая .angle

См. пример (обновлено из слегка унылого предыдущего экземпляра)

Возможно, есть лучшие способы сделать то, что вам нужно, но это должно сработать для вас.

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

Ник


Приложение: Дэн, если вы действительно застряли на этом и можете жить без некоторых вещей, которые дает вам Raphael2, я бы порекомендовал вернуться к Raphael 1.5.x. Преобразования были только что добавлены в Raphael2, код поворота / перевода / масштабирования полностью отличается (и проще) в 1.5.2.

Посмотри на меня, обновляю мой пост, надеясь на карму ...

0 голосов
/ 05 марта 2012

Моей первой мыслью было использование getBBox (false) для захвата координат x, y объекта после преобразования, затем removeChild () исходного объекта Raphael из холста, затем перерисовывание объекта с использованием данных координат из getBBox (false) ). взломать, но у меня это работает.

одно замечание: поскольку объект, который возвращает getBBox (false), является УГЛОВЫМИ координатами (x, y) объекта, вам необходимо вычислить центр перерисованного объекта, выполнив ... x = box ['x'] + (box ['width'] / 2); y = box ['y'] + (box ['height'] / 2);

, где box = shapeObj.getBBox (false);

другой способ решить ту же проблему

...