css - Perché l'ordine delle trasformazioni è importante? Ruota/scala SVG non fornisce lo stesso risultato di scala/ruota




css3 css-transforms (2)

Il modo in cui Temani Afif ha spiegato che segue i sistemi di coordinate che abbraccia ogni trasformazione. Si inizia con il viewport e ogni sistema di coordinate consecutive viene derivato e si trova in un punto diverso sulla tela. Questi sistemi di coordinate potrebbero non essere cartesiani (un "universo allungato"). Sono costruiti nell'albero DOM dall'esterno e, quando incatenati in un attributo, da sinistra a destra.

Ma puoi immaginare la stessa trasformazione anche nella direzione opposta, dall'interno verso l'esterno: prima disegni un rettangolo nel suo sistema di coordinate dello spazio utente cartesiano, e poi lo trasformi con una catena di scale, rotazioni e così via, fino a quando non lo disegni nel sistema di coordinate della finestra, è distorto a qualcos'altro.

Ma se lo guardi in questo modo, le trasformazioni concatenate in un attributo devono essere elaborate da destra a sinistra: transform: scale(2, 1) rotate(10deg) significa prendi un rettangolo, prima ruotalo di 10 gradi, quindi ridimensionare il rettangolo ruotato in direzione orizzontale.

In breve, questi due sono equivalenti:

  • Se si disegna una grafica in un sistema di coordinate trasformato , costruire il sistema di coordinate applicando trasformazioni a questi sistemi di coordinate da sinistra a destra .
  • Se si disegna una grafica trasformata nel sistema di coordinate originale, costruire la grafica applicando le trasformazioni alla grafica da destra a sinistra .

Dopo aver esaminato le specifiche SVG e guide come this e this , faccio ancora fatica a capire esattamente come funzionano le trasformazioni concatenate.

Citazioni rilevanti selezionate

Quando si applica l'attributo transform a un elemento SVG, quell'elemento ottiene una "copia" dell'attuale sistema di coordinate dell'utente in uso.

E:

Quando le trasformazioni sono concatenate, la cosa più importante da tenere presente è che, proprio come con le trasformazioni di elementi HTML, ogni trasformazione viene applicata al sistema di coordinate dopo che tale sistema è stato trasformato dalle trasformazioni precedenti.

E:

Ad esempio, se si applica una rotazione a un elemento, seguita da una traduzione, la traduzione avviene in base al nuovo sistema di coordinate, non a quello iniziale non ruotato.

E:

La sequenza delle trasformazioni è importante. La sequenza in cui sono specificate le funzioni di trasformazione all'interno dell'attributo di trasformazione è la sequenza in cui sono applicate alla forma.

Codice

Il sistema di coordinate corrente del primo rettangolo viene ridimensionato, quindi ruotato (notare l'ordine). Il sistema di coordinate corrente del secondo rettangolo viene ruotato, quindi ridimensionato.

svg {
  border: 1px solid green;
}
<svg xmlns="http://www.w3.org/2000/svg">
  <style>
    rect#s1 {
      fill: red;
      transform: scale(2, 1) rotate(10deg);
    }
  </style>
  <rect id="s1" x="" y="" width="100" height="100" />
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
  <style>
    rect#s2 {
      fill: blue;
      transform: rotate(10deg) scale(2, 1);
    }
  </style>
  <rect id="s2" x="" y="" width="100" height="100" />
</svg>

Domanda

Sappiamo che quando trasformiamo in catena, viene creata una copia del sistema di coordinate corrente in uso per quell'elemento, quindi le trasformazioni vengono applicate nell'ordine in cui sono specificate.

Quando abbiamo un sistema di coordinate utente già ridimensionato e applichiamo una rotazione ad esso, il rettangolo è (come visto) effettivamente inclinato (notare gli angoli modificati). Ciò non accade se eseguiamo le due trasformazioni al contrario (ruotiamo, quindi ridimensioniamo).

Un aiuto esperto su come ruotare il sistema di coordinate della corrente in scala sarebbe molto apprezzato. Sto cercando di capire, da un punto di vista tecnico (meccanismi interni), esattamente il motivo per cui l'inclinazione avviene nel primo rettangolo.

Grazie.


Per illustrare come funziona consideriamo un'animazione per mostrare come l'effetto di ridimensionamento modifica la rotazione.

.red {
  width:80px;
  height:20px;
  background:red;
  margin:80px;
  transform-origin:left center;
  animation: rotate 2s linear infinite;
}
@keyframes rotate {
  from{transform:rotate(0)}
  to{transform:rotate(360deg)}

}
<div class="container">
<div class="red">
</div>
</div>

Come puoi vedere sopra, la rotazione sta creando una perfetta forma circolare.

Ora scaliamo il contenitore e vediamo la differenza:

.red {
  width:80px;
  height:20px;
  background:red;
  margin:80px;
  transform-origin:left center;
  animation: rotate 5s linear infinite;
}
@keyframes rotate {
  from{transform:rotate(0)}
  to{transform:rotate(360deg)}

}
.container {
  display:inline-block;
  transform:scale(3,1);
  transform-origin:left center;
}
<div class="container">
<div class="red">
</div>
</div>

Notate come non abbiamo più un cerchio ma ora è un'ellisse. È come se avessimo preso il cerchio e lo stendch che sta creando l'effetto di inclinazione all'interno del nostro rettangolo.

Se facciamo l'effetto opposto e iniziamo con un effetto scala e quindi applichiamo una rotazione non avremo alcuna inclinazione.

.red {
  width:80px;
  height:20px;
  background:red;
  margin:80px;
  animation: rotate 2s linear infinite;
}
@keyframes rotate {
  from{transform:scale(1,1)}
  to{transform:scale(3,1)}

}
.container {
  display:inline-block;
  transform:rotate(30deg);
  transform-origin:left center;
}
<div class="container">
<div class="red">
</div>
</div>

Per spiegarlo in modo diverso: l'applicazione di una rotazione manterrà lo stesso rapporto tra entrambi gli assi X e Y, quindi non vedrai alcun effetto negativo quando esegui la scala in seguito, ma ridimensionando solo un asse si romperà il rapporto, quindi la nostra forma sembrerà male quando proviamo applicare una rotazione.

Puoi controllare questo link se vuoi maggiori dettagli su come la trasformazione è incatenata e su come la matrice viene coperta: https://www.w3.org/TR/css-transforms-1/#transform-rendering . Riguarda l'elemento HTML ma come detto nella specifica SVG è lo stesso.

Ecco le parti rilevanti:

Le trasformazioni sono cumulative. Cioè, gli elementi stabiliscono il loro sistema di coordinate locale all'interno del sistema di coordinate del loro genitore.

Dal punto di vista dell'utente, un elemento accumula efficacemente tutte le proprietà di trasformazione dei suoi antenati e qualsiasi trasformazione locale applicata ad esso

Facciamo un po 'di matematica per vedere la differenza tra entrambe le trasformazioni. Consideriamo la moltiplicazione di matrici e poiché abbiamo a che fare con una trasformazione lineare 2D lo faremo su ℝ² per semplicità 1 .

Per scale(2, 1) rotate(10deg) avremo

 |2 0|   |cos(10deg) -sin(10deg)|   |2*cos(10deg) -2*sin(10deg) |
 |0 1| x |sin(10deg) cos(10deg) | = |1*sin(10deg) 1*cos(10deg)  |

Ora se applichiamo questa matrice a un (Xi,Yi) otterremo (Xf,Yf) come di seguito:

 Xf = 2* (Xi*cos(10deg) - Yi*sin(10deg))
 Yf =     Xi*sin(10deg) + Yi*cos(10deg)

Nota come l' Xf abbia un moltiplicatore aggiuntivo che è il colpevole della creazione dell'effetto skew. È come se avessimo cambiato il comportamento o Xf e mantenuto Yf

Consideriamo ora la rotate(10deg) scale(2, 1) :

 |cos(10deg) -sin(10deg)|   |2 0|   |2*cos(10deg) -1*sin(10deg) |
 |sin(10deg) cos(10deg) | x |0 1| = |2*sin(10deg) 1*cos(10deg)  |

E poi avremo

 Xf =  2*Xi*cos(10deg) - Yi*sin(10deg)
 Yf =  2*Xi*sin(10deg) + Yi*cos(10deg)

Possiamo considerare 2*Xi come un Xt e possiamo dire che abbiamo ruotato l'elemento ( Xt,Yi ) e questo elemento è stato inizialmente ridimensionato considerando l'asse X.

1 CSS utilizza anche la trasformazione affine (come tradurre), quindi l'uso di ℝ² (coordinate cartesiane) non è sufficiente per eseguire il nostro calcolo, quindi dobbiamo considerare ℝℙ² (coordinate omogenee). Il nostro calcolo precedente sarà:

 |2 0 0|   |cos(10deg) -sin(10deg) 0|   |2*cos(10deg) -2*sin(10deg) 0|
 |0 1 0| x |sin(10deg) cos(10deg)  0| = |1*sin(10deg) 1*cos(10deg)  0|
 |0 0 1|   |0          0           1|   |0            0             1|

In questo caso non cambierà nulla perché la parte affine è nulla ma se abbiamo una traduzione combinata con un'altra trasformazione (es: scale(2, 1) translate(10px,20px) ) avremo quanto segue:

 |2 0 0|   |1 0 10px|   |2 0 20px|
 |0 1 0| x |0 1 20px| = |0 1 20px|
 |0 0 1|   |0 0 1   |   |0 0  1  |

E

Xf =  2*Xi + 20px;
Yf =  Yi + 20px;
1  =  1 (to complete the multiplication) 




css-transforms