Como definir a origem da transformação no SVG

Eu preciso resize e girar certos elementos no documento SVG usando javascript. O problema é que, por padrão, sempre aplica a transformação em torno da origem em (0, 0) – canto superior esquerdo.

Como posso redefinir esse ponto de ancoragem de transformação?

Eu tentei usar o atributo de transform-origin , mas isso não afeta nada.

Foi assim que eu fiz:

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

Não parece definir o ponto crucial para o ponto que eu especifiquei, embora eu possa ver no Firefox que o atributo está definido corretamente. Eu tentei coisas como o center bottom e 50% 100% com e sem parênteses. Nada funcionou até agora.

Alguém pode ajudar?

Para girar use transform="rotate(deg, cx, cy)" , onde deg é o grau que você deseja girar e (cx, cy) define o centro de rotação.

Para escalar / resize, você tem que traduzir por (-cx, -cy), então escalar e então traduzir de volta para (cx, cy). Você pode fazer isso com uma transformação de matriz :

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

Onde sx é o fator de escala no eixo x, sy no eixo y.

Se você pode usar um valor fixo (não “center” ou “50%”), você pode usar 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; 

Alguns navegadores (como o Firefox) não manipularão valores relativos corretamente.

Se você gosta de mim e quer se deslocar e, em seguida, ampliar com a transformação de origem, você precisará de um pouco mais.

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

Aqui está funcionando: http://forresto.github.io/dataflow-prototyping/react/

Para escalar sem ter que usar a transformação de matrix :

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

E aqui está em CSS:

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

Eu tive uma questão semelhante. Mas eu estava usando o D3 para posicionar meus elementos e queria que a transformação e a transição fossem tratadas pelo CSS. Este foi o meu código original, que comecei a trabalhar no 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) ... 

Isso me permitiu definir os valores cx , cy e transform-origin em javascript usando os mesmos dados.

Mas isso não funcionou no Firefox! O que eu tinha que fazer era envolver o circle na tag g e translate lo usando a mesma fórmula de posicionamento acima. Em seguida, anexei o circle na tag g e defini seus valores de cx e cy para 0 . A partir daí, transform: scale(2) seria dimensionado do centro como esperado. O código final ficou assim.

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

Depois de fazer essa alteração, alterei meu CSS para segmentar o circle vez do .dot para adicionar a transform: scale(2) . Eu nem precisei usar transform-origin .

NOTAS:

  1. Eu estou usando d3-selection-multi no segundo exemplo. Isso me permite passar um object para .attrs vez de repetir .attr para cada atributo.

  2. Ao usar um literal de modelo de string, esteja ciente das quebras de linha, conforme ilustrado no primeiro exemplo. Isso includeá uma nova linha na saída e poderá quebrar seu código.