Как установить начало трансформации в SVG

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

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

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

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

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

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

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

8 ответов

Решение

Вращать использовать 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.

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

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

вот так, transform-box: fill-box нет необходимости в сложной матрице

Если вы можете использовать фиксированное значение (не "центр" или "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) не будут правильно обрабатывать относительные значения.

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

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

И вот это в CSS:

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

Если вы похожи на меня и хотите панорамировать и затем изменять масштаб с помощью 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/

Установка атрибута (transform-origin="center") встроенного элемента прямо внутри DOM помогло мне

          <circle
          fill="#FFFFFF"
          cx="82"
          cy="81.625"
          r="81.5"
          transform-origin="center"
        ></circle>

Хорошо работает с использованием преобразований css

Вы можете построить все, что хотите, вокруг источника 0,0, а затем поместить его в группу. <g></g>и матричный перевод всей группы. Таким образом, объект всегда будет вращаться вокруг 0,0 , но вся группа перемещается (переводится) в другое место, и после поворота применяется матрица перемещения.

Это создаст стрелку (например, для датчика), более широкий конец которой находится в точке [0,0], и переместит ее на [160, 200]. Какое бы вращение ни было применено к классу «стрелка», оно повернется вокруг [160, 200].

Протестировано в: Chrome, Firefox, Opera, MS Edge

У меня была похожая проблема. Но я использовал 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. При использовании строкового литерала шаблона учитывайте разрывы строк, как показано в первом примере. Это будет включать новую строку в вывод и может нарушить ваш код.

Другие вопросы по тегам