Рафаэль JS: как переместить / оживить объект пути?

Как-то это не работает...

var paper = Raphael("test", 500, 500);

var testpath = paper.path('M100 100L190 190');

var a = paper.rect(0,0,10,10);
a.attr('fill', 'silver');

a.mousedown( function() {
  testpath.animate({x: 400}, 1000);
});

Я могу перемещать ректы таким образом, но не путями, почему так, и как мне тогда перемещать объект пути?!

6 ответов

Решение

С последней версией Raphael вы можете сделать это:

var _transformedPath = Raphael.transformPath('M100 100L190 190', 'T400,0');
testpath.animate({path: _transformedPath}, 1000);

Это избавляет вас от необходимости clone временный объект.

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

testpath.animate({path:'M400 100L490 190'},1000);

Писать анимацию немного сложнее, но у вас есть преимущество, когда вы получаете вращение и масштабирование бесплатно!

Кстати, я уверен, что это всего лишь пример, но в приведенном выше коде testpath попадает в глобальную область, потому что вы не инициализируете как var testpath

Решено, спасибо Руду!

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

var paper = Raphael("test", 500, 500);

var testpath = paper.path('M100 100L190 190');

var a = paper.rect(0,0,10,10);
a.attr('fill', 'silver');

a.mousedown( function() {

  var temp = testpath.clone();
  temp.translate(400,0);
  testpath.animate({path: temp.attr('path')}, 1000);
  temp.remove();

});

Ответ TimDog был лучшим решением.

Кроме того, просто запомните, что преобразование строки в этом случае означает, что она добавит 400 точек к каждой координате X точки пути / линии и 0 точек к каждой координате Y.

Это означает, M100 100L190 190 превратится в M500 100L590 190,

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

var newCoordinates = [300, 200],
curPos = testpath.path[0],
newPosX = newCoordinates[0] - curPos[1],
newPosY = newCoordinates[1] - curPos[2];

var _transformedPath = Raphael.transformPath(testpath.path, "T"+newPosX+","+newPosY);
testpath.animate({path: _transformedPath});

Надеюсь, это кому-нибудь поможет.

Вот код, который обобщает лучшие из приведенных выше ответов и дает простым путям Рафаэля .attr({pathXY: [newXPos, newYPos]}) атрибут похож на .attr({x: newXPosition}) а также .animate({x: newXPosition}) для фигур.

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


Изменить: код ниже работает в IE7 и IE8. Более ранняя версия этого не удалась в режиме IE8 / VML из-за ошибки Рафаэля, которая возвращает массивы в.attr('path') в режиме SVG, но в.attr('path') в режиме VML.


Код

Добавьте этот код ( Raphael customAttribute и вспомогательную функцию) после определения paper используйте как ниже.

paper.customAttributes.pathXY = function( x,y ) {
  // use with .attr({pathXY: [x,y]});
  // call element.pathXY() before animating with .animate({pathXY: [x,y]})
  var pathArray = Raphael.parsePathString(this.attr('path'));
  var transformArray = ['T', x - this.pathXY('x'), y - this.pathXY('y') ];
    return { 
      path: Raphael.transformPath( pathArray, transformArray) 
    };
};
Raphael.st.pathXY = function(xy) { 
   // pass 'x' or 'y' to get average x or y pos of set
   // pass nothing to initiate set for pathXY animation
   // recursive to work for sets, sets of sets, etc
   var sum = 0, counter = 0;
   this.forEach( function( element ){
     var position = ( element.pathXY(xy) );
     if(position){
       sum += parseFloat(position);
       counter++;
     }
   });
   return (sum / counter);
};
Raphael.el.pathXY = function(xy) {
   // pass 'x' or 'y' to get x or y pos of element
   // pass nothing to initiate element for pathXY animation
   // can use in same way for elements and sets alike
   if(xy == 'x' || xy == 'y'){ // to get x or y of path
     xy = (xy == 'x') ? 1 : 2;
     var pathPos = Raphael.parsePathString(this.attr('path'))[0][xy];
     return pathPos;
   } else { // to initialise a path's pathXY, for animation
     this.attr({pathXY: [this.pathXY('x'),this.pathXY('y')]});
   }
};

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

Для абсолютного перевода (переместиться в фиксированную позицию X,Y) - Live JSBIN demo

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

// moves to x=200, y=300 regardless of previous transformations
path.attr({pathXY: [200,300]});

// moves x only, keeps current y position
path.attr({pathXY: [200,path.pathXY('y')]});

// moves y only, keeps current x position
path.attr({pathXY: [path.pathXY('x'),300]});

Рафаэлю необходимо обрабатывать координаты x и y вместе в одном и том же пользовательском атрибуте, чтобы они могли анимировать вместе и чтобы они были синхронизированы друг с другом.

Для относительного перевода (переместиться на +/- X,Y) - Live JSBIN demo

// moves down, right by 10
path.attr({pathXY: [ path.pathXY('x')+10, path.pathXY('y')+10 ]},500);

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


Для анимации (переместите путь в относительные или абсолютные позиции)

Перед анимацией в первый раз, вам нужно установить значения pathXY из-за ошибки / отсутствующей функции до Raphael 2.1.0, где всем customAttributes нужно дать числовое значение, прежде чем они будут анимированы (в противном случае они превратят каждое число в NaN и ничего не делайте, молча проваливайтесь без ошибок или не оживляйте и прыгайте прямо в конечную позицию).

Перед использованием .animate({pathXY: [newX,newY]});, запустите эту вспомогательную функцию:

somePath.pathXY();

Еще один способ - использовать атрибут transform:

testpath.animate({transform: "t400,0"}, 1000);

переместить путь вправо на 400 пикселей относительно исходного положения.

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

Обратите внимание, что:

  • Атрибут "transform" не зависит от x, y, cx, cy и т. д. Таким образом, эти атрибуты не обновляются анимацией выше.
  • Значение атрибута "transform" всегда основывается на исходной позиции, а не на текущей позиции. Если вы примените анимацию ниже после анимации выше, она переместится на 800px влево относительно, вместо того, чтобы вернуть ее в исходное положение.

    testpath.animate({transform: "t-400,0"}, 1000);
    
Другие вопросы по тегам