Cara menyetel asal transformasi di SVG

104

Saya perlu mengubah ukuran dan memutar elemen tertentu dalam dokumen SVG menggunakan javascript. Masalahnya adalah, secara default, itu selalu menerapkan transformasi di sekitar asal di (0, 0)- kiri atas.

Bagaimana saya bisa mendefinisikan ulang titik jangkar transformasi ini?

Saya mencoba menggunakan transform-originatribut, tetapi tidak mempengaruhi apa pun.

Beginilah cara saya melakukannya:

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

Tampaknya tidak menetapkan titik penting ke titik yang saya tentukan meskipun saya dapat melihat di Firefox bahwa atributnya disetel dengan benar. Saya mencoba hal-hal seperti center bottomdan 50% 100%dengan dan tanpa tanda kurung. Sejauh ini tidak ada yang berhasil.

Adakah yang bisa membantu?

CTheDark
sumber
FWIW, seharusnya diperbaiki pada Firefox 19 beta 3, meskipun saya masih mengalami masalah di Firefox 22. Daftar bugzilla Mozilla: bugzilla.mozilla.org/show_bug.cgi?id=828286
Toph

Jawaban:

147

Untuk memutar gunakan transform="rotate(deg, cx, cy)", di mana deg adalah derajat yang ingin Anda putar dan (cx, cy) tentukan pusat rotasi.

Untuk penskalaan / pengubahan ukuran, Anda harus menerjemahkan dengan (-cx, -cy), lalu menskalakan dan kemudian menerjemahkan kembali ke (cx, cy). Anda dapat melakukan ini dengan transformasi matriks :

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

Di mana sx adalah faktor skala di sumbu x, sy di sumbu y.

Peter Collingridge
sumber
Terima kasih banyak. Putar berfungsi dengan sempurna menurut saya, tetapi penskalaan / pengubahan ukuran selalu berakhir dengan beberapa jumlah acak. Saya mencoba menggunakan matriks dan melakukannya secara terpisah seperti "translate (cx sx, cy sy) scale (sx, sy)". Hasil yang sama.
CTheDark
6
Bukankah itu transform = "matrix (sx, 0, 0, sy, cx-sx cx, cy-sy cy)"? Karena Anda hanya ingin menerjemahkan dengan perbedaan. Saya pikir logika ini benar, tetapi masih memberi saya posisi ...
CTheDark
Sekarang berhasil! Saya hanya menggunakan nilai cx dan cy yang salah! Terima kasih banyak!
CTheDark
1
@JayStevens Ya, transformasi matriks akan berfungsi untuk sistem apa pun, ini hanya matematika. Satu-satunya perbedaan mungkin adalah notasinya. Sejauh yang saya tahu, transformasi matriks CSS menggunakan notasi yang sama seperti untuk SVG, jadi enam angka yang sama dalam urutan itu seharusnya berfungsi.
Peter Collingridge
1
Hanya untuk memperjelas, cx dan cy perlu menjadi offset dari pusat kotak penskalaan Anda. Ini sedikit membuat saya tersandung, seperti yang saya pikirkan dalam model kotak CSS. @forresto menyinggung hal ini dalam jawabannya di bawah ini.
Jason Farnsworth
22

Jika Anda dapat menggunakan nilai tetap (bukan "center" atau "50%"), Anda dapat menggunakan CSS sebagai gantinya:

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

Beberapa browser (seperti Firefox) tidak akan menangani nilai relatif dengan benar.

Hafenkranich
sumber
1
Wow. Akankah +10 jika saya bisa. Ini menyelamatkan hariku! Terima kasih.
Cullub
Bagaimana melakukan ini dan hanya ini di JavaScript?
Andrew S
Suka element.setAttribute('transform-origin', '25px 25px')?
nikk wong
13

Jika Anda seperti saya dan ingin menggeser lalu memperbesar dengan transformasi asal, Anda memerlukan lebih banyak.

// <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);
};

Ini dia bekerja: http://forresto.github.io/dataflow-prototyping/react/

forresto
sumber
Tautan github Anda 404s
NuclearPeon
Halo @NuclearPeon, ini luar biasa, tetapi sepertinya saya tidak dapat menerapkannya dalam kode saya. Elemen SVG yang ingin saya terapkan sudah menjadi variabel yang disebut "mainGrid". Saya mencoba mengganti contoh "view" dengan "mainGrid" tanpa efek. Apa yang saya lewatkan? Terima kasih!
sakeferret
@sakeferret hal yang paling jelas untuk diperiksa adalah idkecocokan dalam atribut getElementByIddan dokumen id="...". Sulit untuk mengetahui secara pasti tanpa melihat kodenya. Jika Anda tidak ingin mempostingnya sebagai pertanyaan SO, Anda dapat mencoba membuat contoh paling sederhana menggunakan nama atribut Anda sampai berfungsi / Anda menemukan masalah, lalu tambahkan sisanya kembali.
NuclearPeon
12

Untuk penskalaan tanpa harus menggunakan matrixtransformasi:

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

Dan ini dia di CSS:

transform: translate(cxpx, cypx) scale(sx, sy) translate(-cxpx, -cypx)
cmititiuc.dll
sumber
1

sepertinya kita semua melewatkan satu trik di sini: transform-box: fill-box

menggunakan transform-box: fill-boxakan membuat elemen dalam SVG berperilaku sebagai elemen HTML biasa. Kemudian Anda dapat melamar transform-origin: center(atau yang lain) seperti biasa

itu benar, transform-box: fill-boxtidak perlu barang matriks yang rumit

George
sumber
Solusi yang SANGAT berguna, ini harus ditingkatkan lebih tinggi. inilah yang saya butuhkan; Terima kasih! Elemen svg dan html memiliki perbedaan halus saat melakukan transformasi dan transisi. Saya bingung dengan bug animasi selama berjam-jam sampai saya menemukan jawaban ini ... dengan transform-box: fill-box;Anda dapat membuat elemen svg menghormati transform-origindengan cara yang waras / seperti yang Anda harapkan!
zfogg
-1

Saya memiliki masalah serupa. Tapi saya menggunakan D3 untuk memposisikan elemen saya, dan ingin transformasi dan transisi ditangani oleh CSS. Ini adalah kode asli saya, yang saya kerjakan di 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) ...

Hal ini memungkinkan saya untuk mengatur cx, cydan transform-originnilai-nilai dalam javascript menggunakan data yang sama.

TAPI ini tidak berhasil di Firefox! Apa yang harus saya lakukan adalah membungkus circledi gtag dan translatemenggunakan rumus posisi yang sama dari atas. Saya kemudian menambahkan circledi gtag, dan mengatur cxdan cynilainya ke 0. Dari sana, transform: scale(2)akan skala dari pusat seperti yang diharapkan. Kode terakhir terlihat seperti ini.

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,
  })

Setelah melakukan perubahan ini, saya mengubah CSS saya untuk menargetkan circlealih - alih .dot, untuk menambahkan transform: scale(2). Saya bahkan tidak perlu menggunakan transform-origin.

CATATAN:

  1. Saya menggunakan d3-selection-multicontoh kedua. Hal ini memungkinkan saya untuk meneruskan objek .attrsdaripada mengulang .attruntuk setiap atribut.

  2. Saat menggunakan literal template string, perhatikan baris baru seperti yang diilustrasikan dalam contoh pertama. Ini akan menyertakan baris baru dalam output dan dapat merusak kode Anda.

Jamie S
sumber
2
Mungkin jika Anda menyetel transform-box: fill-box; Firefox akan bekerja seperti yang Anda inginkan.
Robert Longson
mencobanya. sepertinya tidak berhasil. Ini memang pekerjaan setelah saya mengubah svg:transform-origin.enabledpref di firefox ini about:confighalaman. Tapi ... sepertinya itu bukan solusi yang memadai. Saya juga menemukan perbedaan antara transform-origin: 50% 50%dan transform-origin: center center.
Jamie S
pref svg.transform-origin.enabled telah dihapus banyak versi yang lalu. Kecuali Anda menggunakan versi yang sangat lama, seharusnya tidak ada perbedaan antara 50% dan tengah. Jangan ragu untuk melaporkan bugzilla jika ada perbedaan di Firefox 59 atau yang lebih baru.
Robert Longson