Как установить источник преобразования в SVG


105

Мне нужно изменить размер и повернуть определенные элементы в документе SVG с помощью javascript. Проблема в том, что по умолчанию он всегда применяет преобразование вокруг начала координат (0, 0)вверху слева.

Как я могу переопределить эту точку привязки преобразования?

Я пробовал использовать transform-originатрибут, но он ни на что не влияет.

Вот как я это сделал:

svg.getDocumentById('someId').setAttribute('transform-origin', '75 240');

Кажется, он не устанавливает ключевую точку в указанную мной точку, хотя в Firefox я вижу, что атрибут установлен правильно. Я пробовал такие вещи, как center bottomи 50% 100%со скобками, и без них. Пока ничего не работало.

Кто-нибудь может помочь?


FWIW, предположительно исправлено в Firefox 19 Beta 3, хотя я все еще возникают проблемы в Firefox 22. Mozilla Bugzilla листинг: bugzilla.mozilla.org/show_bug.cgi?id=828286
— Тоф

Ответы:


147

Для поворота используйте transform="rotate(deg, cx, cy)", где deg - это градус, на который вы хотите повернуть, а (cx, cy) определяет центр вращения.

Для масштабирования / изменения размера вам нужно перевести с помощью (-cx, -cy), затем масштабировать, а затем перевести обратно в (cx, cy). Вы можете сделать это с помощью матричного преобразования :

transform="matrix(sx, 0, 0, sy, cx-sx*cx, cy-sy*cy)"

Где sx - коэффициент масштабирования по оси x, sy - по оси y.


Огромное спасибо. Я думаю, что поворот работает отлично, но масштабирование / изменение размера всегда приводит к некоторой случайной величине. Я пробовал использовать матрицу и делать их отдельно, например "translate (cx sx, cy sy) scale (sx, sy)". Тот же результат.
— CTheDark

6
Разве это не было бы transform = "matrix (sx, 0, 0, sy, cx-sx cx, cy-sy cy)"? Потому что вы хотите переводить только по разнице. Я думаю, что эта логика верна, но она все еще
— сбивает

Теперь это работает! Я просто использовал неправильные значения cx и cy! Большое спасибо!
— CTheDark

1
@JayStevens Да, матричное преобразование будет работать для любой системы, это просто математика. Единственная разница может заключаться в обозначениях. Насколько я могу судить, преобразование матрицы CSS использует ту же нотацию, что и для SVG, поэтому должны работать те же шесть чисел в этом порядке.
— Питер Коллингридж

1
Чтобы прояснить, cx и cy должны быть смещениями от центра бокса, в котором вы масштабируете. Это немного сбило меня с толку, поскольку я думал о блочных моделях CSS. @forresto ссылается на это в своем ответе ниже.
— Джейсон Фарнсворт,

22

Если вы можете использовать фиксированное значение (не «центр» или «50%»), вы можете вместо этого использовать CSS:

-moz-transform-origin: 25px 25px;
-ms-transform-origin:  25px 25px;
-o-transform-origin: 25px 25px;
-webkit-transform-origin:  25px 25px;
transform-origin: 25px 25px;

Некоторые браузеры (например, Firefox) некорректно обрабатывают относительные значения.


1
Вот это да. Было бы +10, если бы я мог. Это спасло мне день! Спасибо.
— Cullub

Как это сделать и только это в JavaScript?
— Эндрю С.

Нравится element.setAttribute('transform-origin', '25px 25px')?
— Никк Вонг

13

Если вы похожи на меня и хотите панорамировать, а затем масштабировать с помощью transform-origin, вам понадобится немного больше.

// <g id="view"></g>
var view = document.getElementById("view");

var state = {
  x: 0,
  y: 0,
  scale: 1
};

// Origin of transform, set to mouse position or pinch center
var oX = window.innerWidth/2;
var oY = window.innerHeight/2;

var changeScale = function (scale) {
  // Limit the scale here if you want
  // Zoom and pan transform-origin equivalent
  var scaleD = scale / state.scale;
  var currentX = state.x;
  var currentY = state.y;
  // The magic
  var x = scaleD * (currentX - oX) + oX;
  var y = scaleD * (currentY - oY) + oY;

  state.scale = scale;
  state.x = x;
  state.y = y;

  var transform = "matrix("+scale+",0,0,"+scale+","+x+","+y+")";
  //var transform = "translate("+x+","+y+") scale("+scale+")"; //same
  view.setAttributeNS(null, "transform", transform);
};

Вот он работает: http://forresto.github.io/dataflow-prototyping/react/


Ваша ссылка на github
— 404s

Привет @NuclearPeon, это круто, но я не могу реализовать это в своем коде. Элемент SVG, к которому я хотел бы применить его, уже является переменной с именем mainGrid. Я попытался заменить экземпляр "view" на "mainGrid" безрезультатно. Что мне не хватает? Спасибо!
— sakeferret

@sakeferret наиболее очевидные вещи, которые нужно проверить, - это idсовпадение атрибутов getElementByIdи документов id="...". Трудно узнать наверняка, не увидев кода. Если вы не хотите публиковать его как вопрос SO, вы можете попробовать создать простейший пример, используя свои имена атрибутов, пока он не
— сработает

13

Для масштабирования без использования matrixпреобразования:

transform="translate(cx, cy) scale(sx sy) translate(-cx, -cy)"

И вот это в CSS:

transform: translate(cxpx, cypx) scale(sx, sy) translate(-cxpx, -cypx)

3

похоже, мы все здесь упустили одну хитрость: transform-box: fill-box

использование transform-box: fill-boxзаставит элемент в SVG вести себя как обычный элемент HTML. Затем вы можете подать заявку transform-origin: center(или что-то еще), как обычно

все верно, transform-box: fill-boxникаких сложных матричных штучек


2
ОЧЕНЬ полезное решение, за него можно было бы проголосовать выше. это именно то, что мне нужно; Спасибо! Элементы svg и html имеют небольшие различия при преобразовании и переходе. Я был озадачен ошибкой анимации в течение нескольких часов, пока не нашел этот ответ ... с помощью transform-box: fill-box;вы можете сделать svg elementes уважение transform-originразумным способом / как и следовало ожидать!
— zfogg

-1

У меня была аналогичная проблема. Но я использовал D3 для позиционирования своих элементов и хотел, чтобы преобразование и переход выполнялись с помощью CSS. Это был мой исходный код, с которым я работал в Chrome 65:

//...
this.layerGroups.selectAll('.dot')
  .data(data)
  .enter()
  .append('circle')
  .attr('transform-origin', (d,i)=> `${valueScale(d.value) * Math.sin( sliceSize * i)} 
                                     ${valueScale(d.value) * Math.cos( sliceSize * i + Math.PI)}`)
//... etc (set the cx, cy and r below) ...

Это позволило мне установить cx, cyи transform-originзначение в JavaScript , используя одни и те же данные.

НО это не сработало в Firefox! Что мне нужно было сделать, так это заключить тег circleв gтег и translateиспользовать ту же формулу позиционирования, что и выше. Затем я приложил circleв gтеге, и установить его cxи cyзначение 0. Оттуда transform: scale(2)будет масштабироваться от центра, как и ожидалось. Окончательный код выглядел так.

this.layerGroups.selectAll('.dot')
  .data(data)
  .enter()
  .append('g')
  .attrs({
    class: d => `dot ${d.metric}`,
    transform: (d,i) => `translate(${valueScale(d.value) * Math.sin( sliceSize * i)} ${valueScale(d.value) * Math.cos( sliceSize * i + Math.PI)})`
  })
  .append('circle')
  .attrs({
    r: this.opts.dotRadius,
    cx: 0,
    cy: 0,
  })

После внесения этого изменения я изменил свой CSS, чтобы нацелить его circleвместо .dot, чтобы добавить transform: scale(2). Мне даже не нужно было пользоваться transform-origin.

НОТЫ:

  1. Я использую d3-selection-multiво втором примере. Это позволяет мне передавать объект .attrsвместо повторения .attrдля каждого атрибута.

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


2
Возможно, если вы установите transform-box: fill-box; Firefox работал бы так, как вы хотели.
— Роберт Лонгсон

пробовал это. похоже, не сработало. Он сделал работу после того, как я изменил svg:transform-origin.enabledприв в Фирефоксе about:configстранице. Но ... это не казалось адекватным решением. Я также обнаружил несоответствие между transform-origin: 50% 50%и transform-origin: center center.
— Jamie S

Параметр svg.transform-origin.enabled был удален много версий назад. Если вы не используете очень старую версию, не должно быть разницы между 50% и центром. Не стесняйтесь сообщать об ошибке bugzilla, если в Firefox 59 или более поздних версиях есть отличия.
— Роберт Лонгсон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.