Как установить источник преобразования в 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.