web-dev-qa-db-fra.com

Comment définir la transformation d'origine en SVG

J'ai besoin de redimensionner et de faire pivoter certains éléments d'un document SVG à l'aide de javascript. Le problème est que, par défaut, il applique toujours la transformation autour de l'origine à (0, 0) - en haut à gauche.

Comment puis-je redéfinir ce point d'ancrage de transformation?

J'ai essayé d'utiliser l'attribut transform-Origin, mais cela n'affecte rien. 

Voici comment je l'ai fait:

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

Il ne semble pas que le point pivot soit celui que j’ai spécifié bien que je puisse voir dans Firefox que cet attribut est correctement défini. J'ai essayé des choses comme center bottom et 50% 100% avec et sans parenthèse. Rien n'a fonctionné jusqu'à présent.

Quelqu'un peut-il aider?

78
Charlemagne

Pour faire pivoter, utilisez transform="rotate(deg, cx, cy)", où deg est le degré de rotation souhaité et (cx, cy) définissez le centre de rotation.

Pour la mise à l'échelle/le redimensionnement, vous devez traduire par (-cx, -cy), puis redimensionner et ensuite traduire en (cx, cy). Vous pouvez le faire avec une transformation matrix :

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

Où sx est le facteur d'échelle sur l'axe des x, sy sur l'axe des y.

125
Peter Collingridge

Si vous pouvez utiliser une valeur fixe (pas "center" ou "50%"), vous pouvez utiliser CSS à la place:

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

Certains navigateurs (comme Firefox) ne gèrent pas les valeurs relatives correctement.

18
Hafenkranich

Si vous êtes comme moi et que vous souhaitez effectuer un panoramique puis un zoom avec transform-Origin, vous aurez besoin d'un peu plus.

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

Ici cela fonctionne: http://forresto.github.io/dataflow-prototyping/react/

11
forresto

Pour une mise à l'échelle sans avoir à utiliser la transformation matrix

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

Et le voici en CSS:

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

J'ai eu un problème similaire. Mais j’utilisais D3 pour positionner mes éléments et je voulais que transform et transition soient gérés par CSS. C’était mon code original, que j’ai obtenu sous 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) ...

Cela m'a permis de définir les valeurs cx, cy et transform-Origin en javascript en utilisant les mêmes données. 

MAIS cela n'a pas fonctionné dans Firefox! Ce que je devais faire, c’est d’envelopper circle dans la balise g et translate avec la même formule de positionnement que ci-dessus. J'ai ensuite ajouté la variable circle à la balise g et ai défini ses valeurs cx et cy sur 0. À partir de là, transform: scale(2) évoluerait à partir du centre comme prévu. Le code final ressemblait à ceci.

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

Après avoir apporté cette modification, j'ai modifié mon CSS pour cibler la circle au lieu du .dot, afin d'ajouter la transform: scale(2). Je n'ai même pas eu besoin d'utiliser transform-Origin.

REMARQUES: 

  1. J'utilise d3-selection-multi dans le deuxième exemple. Cela me permet de passer un objet à .attrs au lieu de répéter .attr pour chaque attribut. 

  2. Lorsque vous utilisez un littéral de modèle de chaîne, tenez compte des sauts de ligne, comme illustré dans le premier exemple. Cela inclura une nouvelle ligne dans la sortie et pourrait casser votre code. 

0
Jamie S