Як встановити початок перетворення у SVG


104

Мені потрібно змінити розмір і повернути певні елементи у 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.mozilla.org/show_bug.cgi?id=828286
Toph

Відповіді:


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.


Дуже дякую. Я думаю, що обертання працює ідеально, але масштабування / зміна розміру завжди закінчується на деяку випадкову кількість. Я спробував використовувати матрицю і роблю їх окремо, як "перекласти (cx sx, cy sy) шкала (sx, sy)". Той самий результат.
CTheDark

6
Чи не було б це перетворення = "матриця (sx, 0, 0, sy, cx-sx cx, cy-sy cy)"? Тому що ви хочете перекладати лише різницею. Я вважаю, що ця логіка є правильною, але вона все ще видає мені позиції ...
CTheDark

Тепер це працює! Я просто використовував неправильні значення cx та cy! Дуже дякую!
CTheDark

1
@JayStevens Так, перетворення матриці буде працювати для будь-якої системи, це лише математика. Єдиною різницею може бути позначення. Наскільки я можу сказати, перетворення матриці CSS використовує те саме позначення, що і для SVG, тому ті ж шість чисел у цьому порядку повинні працювати.
Пітер Коллінгрідж

1
Просто для уточнення, cx і cy повинні бути зміщені в центрі поля, в якому ви масштабуєте. Це трохи потішило мене, як я думав у моделях CSS box. @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?
Андрій S

Як element.setAttribute('transform-origin', '25px 25px')?
nikk wong

13

Якщо ви схожі на мене і хочете зробити панорамування, а потім збільшити масштаб із трансформаційним походженням, вам знадобиться трохи більше.

// <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 Найбільш очевидні речі, на які слід перевірити, - це idвідповідність атрибутів getElementByIdта документів id="...". Це важко точно знати, не бачачи коду. Якщо ви не хочете розміщувати це як питання SO, ви можете спробувати створити найпростіший приклад, використовуючи імена атрибутів, поки він не працює / не знайдете проблему, а потім додайте решту ще раз.
NuclearPeon

12

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

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

І ось це в CSS:

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

1

схоже, ми тут пропустили трюк: transform-box: fill-box

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

це правильно, transform-box: fill-boxне потрібно ніяких складних матеріалів матриці


ДУЖЕ корисне рішення, це може бути підвищено. це саме те, що мені було потрібно; спасибі! елементи svg та html мають тонкі відмінності при перетворенні та переході. Мене годинами здивувала помилка з анімацією, поки я не знайшов цю відповідь ... з transform-box: fill-box;тобою можна змусити поважати svg елементи 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
Можливо, якщо ви встановите трансформер: fill-box; Firefox працював би так, як ви цього хотіли.
Роберт Лонгсон

спробував це. не здавалося, працює. Він зробив роботу після того, як я змінив svg:transform-origin.enabledприв в Фірефоксе about:configсторінці. Але ... це не здавалося адекватним рішенням. Я також виявив розбіжність між transform-origin: 50% 50%та transform-origin: center center.
Джеймі S

preg. sv.transform-origin.enabled pref було видалено багато версій тому. Якщо ви не використовуєте дуже стару версію, між 50% і центром не повинно бути різниці. Не соромтеся піднімати помилку на випадок, якщо є різниця у Firefox 59 або пізнішої версії.
Роберт Лонгсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.