SVG'de dönüşüm kaynağı nasıl ayarlanır


105

Javascript kullanarak SVG belgesindeki belirli öğeleri yeniden boyutlandırmam ve döndürmem gerekiyor. Sorun, varsayılan olarak, dönüşümü her zaman (0, 0)sol üstteki orijinin etrafına uygulamasıdır .

Bu dönüşüm bağlantı noktasını nasıl yeniden tanımlayabilirim?

transform-originÖzniteliği kullanmayı denedim , ancak hiçbir şeyi etkilemiyor.

Ben böyle yaptım:

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

Firefox'ta özniteliğin doğru şekilde ayarlandığını görebilmeme rağmen, ana noktayı belirttiğim noktaya ayarlamıyor gibi görünüyor. Gibi şeyler denedim center bottomve 50% 100%ile ve parantez olmadan. Şimdiye kadar hiçbir şey işe yaramadı.

Biri yardım edebilir mi?


FWIW, sözde Firefox 19 beta 3'te düzeltildi, ancak hala Firefox 22'de sorun yaşıyorum. Mozilla bugzilla listesi: bugzilla.mozilla.org/show_bug.cgi?id=828286
Toph

Yanıtlar:


148

Döndürmek için transform="rotate(deg, cx, cy)", burada derece döndürmek istediğiniz derecedir ve (cx, cy) dönüş merkezini tanımlar.

Ölçekleme / yeniden boyutlandırma için, (-cx, -cy) ile çevirmeniz, ardından ölçeklendirmeniz ve ardından (cx, cy) 'ye geri çevirmeniz gerekir. Bunu bir matris dönüşümü ile yapabilirsiniz :

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

Burada sx, x ekseninde ölçekleme faktörü, y ekseninde sy.


Çok teşekkür ederim. Bence döndürme mükemmel çalışıyor, ancak ölçekleme / yeniden boyutlandırma her zaman rastgele bir miktarla sonuçlanıyor. Matrisi kullanmayı ve bunları "translate (cx sx, cy sy) scale (sx, sy)" gibi ayrı ayrı yapmayı denedim . Aynı sonuç.
CTheDark

6
Dönüşüm = "matrix (sx, 0, 0, sy, cx-sx cx, cy-sy cy)" olmaz mıydı? Çünkü sadece farkla çevirmek istiyorsunuz. Bu mantığın doğru olduğunu düşünüyorum, ancak yine de bana konum veriyor ...
CTheDark

Şimdi çalışıyor! Yanlış cx ve cy değerlerini kullanıyordum! Çok teşekkürler!
CTheDark

1
@JayStevens Evet, matris dönüşümü herhangi bir sistem için çalışacaktır, bu sadece matematiktir. Tek fark, gösterim olabilir. Anlayabildiğim kadarıyla, CSS matris dönüşümü SVG'lerle aynı gösterimi kullanıyor, bu nedenle bu sıradaki aynı altı sayı çalışmalıdır.
Peter Collingridge

1
Sadece açıklığa kavuşturmak için, cx ve cy , ölçeklendirdiğiniz kutunun ortasına denk gelmelidir . Bu, CSS kutu modellerinde düşündüğüm gibi, beni biraz tedirgin etti. @forresto aşağıdaki cevabında bunu ima ediyor.
Jason Farnsworth

22

Sabit bir değer kullanabiliyorsanız ("merkez" veya "% 50" değil), bunun yerine CSS kullanabilirsiniz:

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

Bazı tarayıcılar (Firefox gibi) göreli değerleri doğru şekilde işlemez.


1
Vay. Yapabilseydim +10 olurdu. Bu benim günümü kurtardı! Teşekkürler.
Cullub

Bunu nasıl yapmalı ve sadece bu JavaScript'te?
Andrew S

Beğen element.setAttribute('transform-origin', '25px 25px')?
nikk wong

13

Siz de benim gibiyseniz ve yatay kaydırmak ve ardından dönüşüm orijini ile yakınlaştırmak istiyorsanız, biraz daha fazlasına ihtiyacınız olacak.

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

İşte çalışıyor: http://forresto.github.io/dataflow-prototyping/react/



Merhaba @NuclearPeon, bu harika, ancak bunu koduma uygulayamıyorum. Başvurmak istediğim SVG öğesi zaten "mainGrid" adlı bir değişkendir. Etkisiz "view" örneğini "mainGrid" ile değiştirmeyi denedim. Neyi kaçırıyorum? Teşekkürler!
sakeferret

@sakeferret kontrol edilmesi gereken en bariz şey id, getElementByIdve belgelerin id="..."özniteliğindeki eşleşmelerdir . Kodu görmeden kesin olarak bilmek zor. SO sorusu olarak göndermek istemiyorsanız, işe
yarayana

13

matrixDönüşümü kullanmak zorunda kalmadan ölçekleme için :

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

Ve işte CSS'de:

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

3

Görünüşe göre burada hepimiz bir numara kaçırdık: transform-box: fill-box

kullanmak transform-box: fill-box, bir SVG içindeki bir öğenin normal bir HTML öğesi gibi davranmasını sağlar. Daha sonra transform-origin: centernormalde yaptığınız gibi başvurabilirsiniz (veya başka bir şey)

bu doğru, transform-box: fill-boxkarmaşık matris maddelerine gerek yok


2
ÇOK kullanışlı bir çözüm, bu daha yüksek oylanmalıdır. tam da ihtiyacım olan şey bu; teşekkür ederim! svg ve html öğeleri dönüştürme ve geçiş sırasında ince farklılıklara sahiptir. Bu cevabı bulana kadar saatlerce bir animasyon transform-box: fill-box;hatasıyla kafam karıştı ... svg öğelerini transform-originaklı başında bir şekilde / beklediğiniz gibi saygı gösterebilirsiniz !
zfogg

-1

Benzer bir sorun yaşadım. Ancak öğelerimi konumlandırmak için D3 kullanıyordum ve dönüşüm ve geçişin CSS tarafından ele alınmasını istedim . Bu, Chrome 65'te çalıştığım orijinal kodumdu:

//...
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) ...

Bu beni ayarlamalarına izin cx, cyve transform-originaynı verileri kullanarak javascript değerleri.

AMA bu Firefox'ta işe yaramadı! Ne yapmak zorunda sarmak oldu circleyılında getiketi ve translatebunun yukarıdan aynı konumlandırma formülü kullanarak. Sonra eklenen circleiçinde getiketi ve onun set cxve cydeğerleri 0. Oradan, transform: scale(2)beklendiği gibi merkezden ölçeklenir. Son kod şuna benziyordu.

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

Bu değişikliği yaptıktan sonra, hedeflediğim benim CSS değişti circleyerine .doteklemek için, transform: scale(2). Kullanmaya bile ihtiyacım yoktu transform-origin.

NOTLAR:

  1. Ben kullanıyorum d3-selection-multiİkinci örnekte. Bu, her öznitelik için .attrsyinelemek yerine bir nesneyi iletmeme izin veriyor .attr.

  2. Bir dize şablonu hazır bilgisi kullanırken, ilk örnekte gösterildiği gibi satır sonlarına dikkat edin. Bu, çıktıda bir satırsonu içerir ve kodunuzu bozabilir.


2
Belki de transform-box: fill-box ayarlarsanız; Firefox, istediğiniz şekilde çalışırdı.
Robert Longson

bunu denedim. işe yaramadı. Bu yaptığımız i değiştirdikten sonra çalışmalarını svg:transform-origin.enabledFirefox'un içinde pref about:configsayfasında. Ama ... bu yeterli bir çözüm gibi görünmüyordu. Ayrıca transform-origin: 50% 50%ve arasında bir tutarsızlık buldum transform-origin: center center.
Jamie S

svg.transform-origin.enabled pref birçok sürümden önce kaldırıldı. Çok eski bir sürüm kullanmadığınız sürece% 50 ile merkez arasında hiçbir fark olmamalıdır. Firefox 59 veya sonraki sürümlerde bir fark varsa, bir bugzilla hatası oluşturmaktan çekinmeyin.
Robert Longson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.