Di recente ho creato un modello di muro di mattoni come parte del mio #Piccoli modelli series, una sfida in cui creo motivi o trame dall'aspetto organico in SVG entro 560 byte (o circa la dimensione di due tweet). Per soddisfare questo vincolo, ho attraversato un viaggio che mi ha insegnato alcuni modi radicali per ottimizzare i modelli SVG in modo che contengano il minor numero di codice possibile senza influire sulla qualità complessiva dell'immagine.
Voglio guidarti attraverso il processo e mostrarti come possiamo prendere un modello SVG che parte da 197 byte fino a soli 44 byte: un'enorme riduzione del 77.7%!
Il modello SVG
Questo è ciò che viene chiamato un pattern di mattoni "running bond". È lo schema di mattoni più comune là fuori, e uno che sicuramente hai visto prima: ogni fila di mattoni è sfalsata di metà della lunghezza di un mattone, creando uno schema sfalsato ripetuto. La disposizione è piuttosto semplice, rendendo SVG <pattern>
elemento una misura perfetta per riprodurlo nel codice.
L'SVG <pattern>
element utilizza un oggetto grafico predefinito che può essere replicato (o “affiancato”) ad intervalli fissi lungo gli assi orizzontale e verticale. In sostanza, definiamo un motivo a tessere rettangolare e viene ripetuto per dipingere l'area di riempimento.
Per prima cosa, impostiamo le dimensioni di un mattone e lo spazio tra ogni mattone. Per semplicità, usiamo numeri puliti e rotondi: una larghezza di 100
e un'altezza di 30
per il mattone, e 10
per gli spazi orizzontali e verticali tra di loro.
Successivamente, dobbiamo identificare la nostra tessera "base". E per "piastrella" sto parlando di piastrelle con motivi piuttosto che di piastrelle fisiche, da non confondere con i mattoni. Usiamo la parte evidenziata dell'immagine sopra come piastrella del modello: due mattoni interi nella prima fila e uno intero inserito tra due mezzi mattoni nella seconda fila. Nota come e dove sono inclusi gli spazi vuoti, perché quelli devono essere inclusi nel riquadro del motivo ripetuto.
Quando si usa <pattern>
, dobbiamo definire i pattern width
ed height
, che corrispondono alla larghezza e all'altezza della piastrella di base. Per ottenere le dimensioni, abbiamo bisogno di un po' di matematica:
Tile Width = 2(Brick Width) + 2(Gap) = 2(100) + 2(10) = 220
Tile Height = 2(Bright Height) + 2(Gap) = 2(30) + 2(10) = 80
Va bene, così è la nostra tessera modello 220✕80
. Dobbiamo anche impostare il patternUnits
attributo, dove il valore userSpaceOnUse
essenzialmente significa pixel. Infine, aggiungendo un id
al modello è necessario in modo che possa essere referenziato quando dipingiamo un altro elemento con esso.
<pattern id="p" width="220" height="80" patternUnits="userSpaceOnUse"> <!-- pattern content here -->
</pattern>
Ora che abbiamo stabilito le dimensioni del riquadro, la sfida è creare il codice per il riquadro in modo da rendere la grafica con il minor numero di byte possibile. Questo è ciò che speriamo di ottenere alla fine:
Markup iniziale (197 byte)
L'approccio più semplice e dichiarativo per ricreare questo schema che mi viene in mente è disegnare cinque rettangoli. Per impostazione predefinita, il fill
di un elemento SVG è nero e il file stroke
è trasparente. Funziona bene per ottimizzare i modelli SVG, poiché non dobbiamo dichiararli esplicitamente nel codice.
Ogni riga nel codice seguente definisce un rettangolo. Il width
ed height
sono sempre impostati, e il x
ed y
le posizioni vengono impostate solo se un rettangolo è sfalsato rispetto a 0
posizione.
<rect width="100" height="30"/>
<rect x="110" width="100" height="30"/>
<rect y="40" width="45" height="30"/>
<rect x="55" y="40" width="100" height="30"/>
<rect x="165" y="40" width="55" height="30"/>
La fila superiore della piastrella conteneva due mattoni a tutta larghezza, il secondo mattone è posizionato su x="110"
permettendo 10
pixel di spazio prima del mattone. Allo stesso modo c'è 10
pixel di spazio dopo, perché il mattone finisce a 210
pixel (110 + 100 = 210
) sull'asse orizzontale anche se il <pattern>
la larghezza è 220
pixel. Abbiamo bisogno di quel po' di spazio in più; altrimenti il secondo mattone si fonderebbe con il primo mattone della piastrella adiacente.
I mattoni nella seconda riga (in basso) sono sfalsati in modo che la riga contenga due mezzi mattoni e un mattone intero. In questo caso, vogliamo che i mattoni di mezza larghezza si uniscano in modo che non ci siano spazi vuoti all'inizio o alla fine, consentendo loro di fluire senza soluzione di continuità con i mattoni nelle tessere del modello adiacenti. Quando si compensano questi mattoni, dobbiamo anche includere i mezzi spazi vuoti, quindi il x
i valori sono 55
ed 165
, Rispettivamente.
Riutilizzo degli elementi, (-43B, 154B in totale)
Sembra inefficiente definire ogni mattone in modo così esplicito. Non c'è un modo per ottimizzare i modelli SVG riutilizzando invece le forme?
Non credo che sia ampiamente noto che SVG abbia un file <use>
elemento. Puoi fare riferimento a un altro elemento con esso e rendere quell'elemento di riferimento ovunque <use>
viene usato. Ciò consente di risparmiare parecchi byte perché possiamo omettere di specificare le larghezze e le altezze di ogni mattone, ad eccezione del primo.
Detto questo, <use>
viene fornito con un piccolo prezzo. Cioè, dobbiamo aggiungere un id
per l'elemento che vogliamo riutilizzare.
<rect id="b" width="100" height="30"/>
<use href="#b" x="110"/>
<use href="#b" x="-55" y="40"/>
<use href="#b" x="55" y="40"/>
<use href="#b" x="165" y="40"/>
Il più corto id
possibile è un carattere, quindi ho scelto "b" per mattone. Il <use>
l'elemento può essere posizionato in modo simile a <rect>
, Con l' x
ed y
attributi come offset. Dal momento che ogni mattone è a tutta larghezza ora che siamo passati a <use>
(ricorda, abbiamo esplicitamente dimezzato i mattoni nella seconda fila della tessera del modello), dobbiamo usare un negativo x
valore nella seconda riga, quindi assicurati che l'ultimo mattone trabocchi dalla piastrella per quella connessione senza soluzione di continuità tra i mattoni. Questi vanno bene, però, perché tutto ciò che cade al di fuori del riquadro del motivo viene automaticamente tagliato.
Riesci a individuare alcune stringhe ripetute che possono essere scritte in modo più efficiente? Lavoriamo su quelli dopo.
Riscrittura nel percorso (-54B, 100B in totale)
<path>
è probabilmente l'elemento più potente in SVG. Puoi disegnare praticamente qualsiasi forma con "comandi" al suo interno d
attributo. Sono disponibili 20 comandi, ma abbiamo bisogno solo dei più semplici per i rettangoli.
Ecco dove sono atterrato con quello:
<path d="M0 0h100v30h-100z M110 0h100v30h-100 M0 40h45v30h-45z M55 40h100v30h-100z M165 40h55v30h-55z"/>
Lo so, numeri e lettere super strani! Hanno tutti un significato, Certo. Ecco cosa sta succedendo in questo caso specifico:
M{x} {y}
: Si sposta su un punto in base alle coordinate.z
: Chiude il segmento corrente.h{x}
: Disegna una linea orizzontale dal punto corrente, con la lunghezza dix
nella direzione definita dal segno dix
. Minuscolox
indica una coordinata relativa.v{y}
: Disegna una linea verticale dal punto corrente, con la lunghezza diy
nella direzione definita dal segno diy
. Minuscoloy
indica una coordinata relativa.
Questo markup è molto più conciso del precedente (le interruzioni di riga e gli spazi di indentazione sono solo per la leggibilità). E, ehi, siamo riusciti a ritagliare metà della dimensione iniziale, arrivando a 100 byte. Eppure, qualcosa mi fa pensare che questo potrebbe essere più piccolo...
Revisione tile (-38B, 62B in totale)
La nostra tessera del modello non ha parti ripetute? È chiaro che nella prima fila si ripete un intero mattone, ma che dire della seconda fila? È un po' più difficile da vedere, ma se tagliamo a metà il mattone centrale diventa ovvio.
Bene, il mattone centrale non è esattamente tagliato a metà. C'è un leggero offset perché dobbiamo anche tenere conto del gap. Ad ogni modo, abbiamo appena trovato un pattern di tile di base più semplice, il che significa meno byte! Questo significa anche che dobbiamo dimezzare il width
della nostra <pattern>
elemento da 220 a 110.
<pattern id="p" width="110" height="80" patternUnits="userSpaceOnUse"> <!-- pattern content here -->
</pattern>
Ora vediamo come viene disegnata la tessera semplificata <path>
:
<path d="M0 0h100v30h-100z M0 40h45v30h-45z M55 40h55v30h-55z"/>
La dimensione è ridotta a 62 byte, che è già meno di un terzo della dimensione originale! Ma perché fermarsi qui quando c'è ancora di più che possiamo fare!
Comandi di accorciamento del percorso (-9B, 53B in totale)
Vale la pena approfondire un po' <path>
elemento perché fornisce ulteriori suggerimenti per l'ottimizzazione dei modelli SVG. Un malinteso che ho avuto quando ho lavorato con <path>
riguarda come il fill
attributo funziona. Avendo giocato molto con MS Paint durante la mia infanzia, ho imparato che qualsiasi forma che voglio riempire con un colore solido deve essere chiusa, cioè non avere punti in sospeso. Altrimenti, la vernice fuoriesce dalla forma e si rovescia su tutto.
In SVG, tuttavia, questo non è vero. Lasciami citare la spec stessa:
L'operazione di riempimento riempie i sottotracciati aperti eseguendo l'operazione di riempimento come se fosse stato aggiunto un ulteriore comando "chiudipercorso" al tracciato per collegare l'ultimo punto del sottotracciato con il primo punto del sottotracciato.
Ciò significa che possiamo omettere i comandi di chiusura del percorso (z
), perché i sottotracciati sono considerati automaticamente chiusi una volta riempiti.
Un'altra cosa utile da sapere sui comandi di percorso è che sono disponibili in varianti maiuscole e minuscole. Le lettere minuscole indicano che vengono utilizzate le coordinate relative; le lettere maiuscole indicano invece che vengono utilizzate coordinate assolute.
È un po' più complicato di quello con il H
ed V
comandi perché includono solo una coordinata. Ecco come descriverei questi due comandi:
H{x}
: Disegna una linea orizzontale dal punto corrente da coordinarex
.V{y}
: Disegna una linea verticale dal punto corrente da coordinarey
.
Quando disegniamo il primo mattone nella tessera del modello, iniziamo dal (0,0)
coordinate. Quindi tracciamo una linea orizzontale su (100,0)
e una linea verticale a (100,30)
e, infine, disegna una linea orizzontale su (0,30)
. Abbiamo usato il h-100
comando nell'ultima riga, ma è l'equivalente di H0
, che è di due byte invece di cinque. Possiamo sostituire due occorrenze simili e pareggiare il codice del nostro <path>
fino a questo:
<path d="M0 0h100v30H0 M0 40h45v30H0 M55 40h55v30H55"/>
Altri 9 byte sono stati eliminati: quanto più piccolo possiamo andare?
Bridging (-5 miliardi, 48 miliardi in totale)
I comandi più lunghi che ostacolano un modello SVG completamente ottimizzato sono i comandi "sposta in" che occupano rispettivamente 4, 5 e 6 byte. Un vincolo che abbiamo è che:
Un segmento di dati di percorso (se presente) deve iniziare con un comando "moveto".
Ma va bene. Il primo è comunque il più corto. Se scambiamo le righe, possiamo trovare una definizione del percorso in cui dobbiamo solo muoverci orizzontalmente o verticalmente tra i mattoni. E se potessimo usare il h
ed v
comandi lì invece di M
?
Il diagramma sopra mostra come le tre forme possono essere disegnate con un unico percorso. Si noti che stiamo sfruttando il fatto che il fill
l'operazione chiude automaticamente la parte aperta in mezzo (110,0)
ed (0,0)
. Con questa riorganizzazione, abbiamo anche spostato lo spazio vuoto a sinistra del mattone a larghezza intera nella seconda fila. Ecco come appare il codice, ancora suddiviso in un mattone per riga:
<path d="M0 0v30h50V0 h10v30h50 v10H10v30h100V0"/>
Sicuramente, abbiamo trovato la soluzione più piccola in assoluto ora che siamo scesi a 48 byte, giusto?! Bene…
Rifilatura cifre (-4B, 44B in totale)
Se puoi essere un po' flessibile con le dimensioni, c'è un altro piccolo modo in cui possiamo ottimizzare i modelli SVG. Abbiamo lavorato con una larghezza del mattone di 100
pixel, ma sono tre byte. Cambiandolo in 90
significa un byte in meno ogni volta che dobbiamo scriverlo. Allo stesso modo, abbiamo utilizzato un gap di 10
pixel — ma se lo cambiamo in 8
invece, salviamo un byte su ciascuna di queste occorrenze.
<path d="M0 0v30h45V0 h8v30h45 v8H8v30h90V0"/>
Naturalmente, questo significa anche che dobbiamo adattare le dimensioni del modello di conseguenza. Ecco il codice del modello SVG ottimizzato finale:
<pattern id="p" width="98" height="76" patternUnits="userSpaceOnUse"> <path d="M0 0v30h45V0h8v30h45v8H8v30h90V0"/>
</pattern>
La seconda riga nello snippet sopra, senza contare i rientri, è 44 byte. Siamo arrivati qui da 197 byte in sei iterazioni. È un grosso Riduzione delle dimensioni del 77.7%.!
Mi chiedo però... è davvero la dimensione più piccola possibile? Abbiamo esaminato tutti i modi possibili per ottimizzare i modelli SVG?
Ti invito a provare a minimizzare ulteriormente questo codice, o anche a sperimentare metodi alternativi per ottimizzare i pattern SVG. Mi piacerebbe vedere se riuscissimo a trovare il vero minimo globale con la saggezza della folla!
Ulteriori informazioni sulla creazione e l'ottimizzazione dei modelli SVG
Se sei interessato a saperne di più sulla creazione e l'ottimizzazione dei modelli SVG, leggi il mio articolo su creare modelli con filtri SVG. Oppure, se vuoi dare un'occhiata a una galleria di oltre 60 modelli, puoi visualizzare il Collezione PetitePatterns CodePen. Infine, puoi guardare i miei tutorial su YouTube per aiutarti ad approfondire ulteriormente i modelli SVG.
Ottimizzazione dei modelli SVG alla loro dimensione più piccola originariamente pubblicato il CSS-Tricks. Dovresti ricevi la newsletter.
- "
- 10
- 100
- 77
- 9
- 98
- Chi siamo
- Assoluta
- Il mio account
- aggiuntivo
- Tutti
- Consentire
- già
- Un altro
- approccio
- RISERVATA
- articolo
- gli attributi
- disponibile
- ASSI
- Po
- Nero
- Challenge
- il cambiamento
- chiuso
- codice
- Uncommon
- veloce
- contiene
- contenuto
- coordinare
- potuto
- Creazione
- Corrente
- dati
- più profondo
- giù
- finisce
- sviluppate
- qualunque cosa
- esempio
- Tranne
- esperimento
- Infine
- Nome
- in forma
- flusso
- essere trovato
- ulteriormente
- divario
- ottenere
- globali
- avendo
- altezza
- Aiuto
- qui
- Evidenziato
- Come
- HTTPS
- identificare
- Immagine
- includere
- incluso
- IT
- stessa
- conosciuto
- perdita
- IMPARARE
- imparato
- leveraging
- linea
- piccolo
- guardò
- amore
- FA
- Fare
- gestito
- matematica
- mente
- Scopri di più
- maggior parte
- cambiano
- MS
- numero
- numeri
- offset
- Va bene
- aprire
- ottimizzati
- altrimenti
- Cartamodello
- Fisico
- punto
- posizionato
- possibile
- potente
- piuttosto
- prezzo
- processi
- fornisce
- qualità
- tondo
- running
- Suddetto
- senza soluzione di continuità
- Serie
- set
- forme
- simile
- Un'espansione
- SIX
- Taglia
- So
- soluzione
- qualcosa
- lo spazio
- Spot
- inizia a
- inizio
- supportato
- parlando
- Attraverso
- top
- trasparente
- esercitazioni
- uso
- APPREZZIAMO
- Visualizza
- W3
- Orologio
- il benvenuto
- Che
- entro
- senza
- Lavora
- lavoro
- lavori
- valore
- youtube