Optimice las plantillas SVG al tamaño más pequeño trucos CSS
Recientemente creé un modelo de una pared de ladrillos como parte de la mía. #PatronesPequeños serie, un desafío en el que creo patrones o texturas de aspecto orgánico en SVG dentro de 560 bytes (o aproximadamente el tamaño de dos tweets). Para cumplir con esta limitación, emprendí un viaje que me enseñó algunas formas radicales de optimizar los modelos SVG para que contengan la menor cantidad de código posible sin comprometer la calidad general de la imagen.
Quiero guiarlo a través del proceso y mostrarle cómo podemos obtener un modelo SVG que comienza desde 197 bytes hasta solo 44 bytes: ¡un gran descuento del 77,7 %!
modelo SVG
Esto es lo que se llama un modelo de ladrillo de "conexión en funcionamiento". Este es el patrón de ladrillos más común allí y uno que probablemente hayas visto antes: cada fila de ladrillos se desplaza la mitad de la longitud del ladrillo, creando un patrón de tablero de ajedrez repetitivo. El diseño es bastante simple, lo que hace que SVG <pattern>
El elemento encaja perfectamente para reproducirlo en código.
SVG <pattern>
el elemento utiliza un objeto gráfico predefinido que se puede replicar (o "mosaico") a intervalos fijos a lo largo de los ejes horizontal y vertical. En esencia, definimos un patrón rectangular de mosaicos y se repite para dibujar el área de relleno.
Primero, establezcamos el tamaño del ladrillo y la distancia entre cada ladrillo. Para simplificar, usemos números redondos puros: ancho de 100
y altura desde 30
para ladrillo y 10
para los espacios horizontales y verticales entre ellos.
Luego necesitamos identificar nuestro mosaico "base". Y bajo "mosaico" estoy hablando de mosaicos de plantilla, no mosaicos físicos, que no deben confundirse con ladrillos. Usemos la parte resaltada de la imagen de arriba como nuestro mosaico estampado: dos ladrillos enteros en la primera fila y un entero intercalado entre dos medios ladrillos en la segunda fila. Obtenga un mosaico con una plantilla.
Cuando se usa <pattern>
necesitamos definir la plantilla width
y height
que corresponden al ancho y alto de la tesela base. Para obtener las dimensiones, necesitamos un poco de matemática:
Tile Width = 2(Brick Width) + 2(Gap) = 2(100) + 2(10) = 220
Tile Height = 2(Bright Height) + 2(Gap) = 2(30) + 2(10) = 80
Bien, nuestro mosaico de plantilla es 220✕80
También tenemos que preguntar patternUnits
atributo donde el valor userSpaceOnUse
esencialmente significa píxeles. Finalmente, agregue id
es necesario a la plantilla para que pueda ser referenciada cuando dibujemos otro elemento con ella.
<pattern id="p" width="220" height="80" patternUnits="userSpaceOnUse">
<!-- pattern content here -->
</pattern>
Ahora que hemos determinado las dimensiones del mosaico, el desafío es crear el código para el mosaico de manera que muestre el gráfico con la menor cantidad de bytes posible. Esto es lo que esperamos obtener al final:
Marcado inicial (197 bytes)
El enfoque más simple y declarativo para recrear este modelo que se me ocurre es dibujar cinco rectángulos. Por defecto, fill
el elemento SVG es negro y stroke
es transparente Esto funciona bien para optimizar los modelos SVG, ya que no necesitamos declararlos explícitamente en el código.
Cada línea en el siguiente código define un rectángulo width
y height
siempre están establecidos y x
y y
las posiciones se establecen sólo si el rectángulo está desplazado por 0
posición.
<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 superior de mosaicos contiene dos ladrillos de ancho completo, el segundo ladrillo se coloca junto a x="110"
permitiendo 10
brecha de píxeles antes del ladrillo. De la misma manera hay 10
brecha de píxeles después porque el ladrillo termina en 210
píxeles (110 + 100 = 210
) a lo largo del eje horizontal, aunque <pattern>
el ancho es 220
píxeles Necesitamos este pequeño espacio extra; de lo contrario, el segundo ladrillo se fusionará con el primer ladrillo en el mosaico adyacente.
Los ladrillos de la segunda fila (inferior) están desplazados de modo que la fila contenga dos medios ladrillos y un ladrillo entero. En este caso, queremos que los ladrillos de medio ancho se fusionen para que no haya espacio al principio o al final, lo que les permite combinarse perfectamente con los ladrillos en los mosaicos de plantilla adyacentes. Al compensar estos ladrillos, también debemos incluir la mitad de los espacios, de esta manera x
los valores son 55
y 165
respectivamente.
Reutilización de un artículo, (-43B, total 154B)
Parece ineficaz definir cada ladrillo tan explícitamente. ¿No hay alguna forma de optimizar los modelos SVG reutilizando formularios?
No creo que sea muy conocido que SVG tiene <use>
Puede hacer referencia a otro elemento con él y mostrar este elemento al que se hace referencia en cualquier lugar <use>
Esto ahorra muchos bytes, porque podemos perder la especificación de los anchos y altos de cada ladrillo, excepto el primero.
dijo eso <use>
viene con un pequeño precio. Es decir, debemos agregar id
por el elemento que queremos reutilizar.
<rect id="b" width="100" height="30"/>
<use href="https://css-tricks.com/optimizing-svg-patterns/#b" x="110"/>
<use href="https://css-tricks.com/optimizing-svg-patterns/#b" x="-55" y="40"/>
<use href="https://css-tricks.com/optimizing-svg-patterns/#b" x="55" y="40"/>
<use href="https://css-tricks.com/optimizing-svg-patterns/#b" x="165" y="40"/>
El más corto id
un signo es posible, así que elegí "b" para el ladrillo <use>
el elemento se puede colocar de forma similar a <rect>
s x
y y
atributos tales como compensaciones. Porque cada ladrillo tiene el ancho completo ahora que hemos avanzado <use>
(recuerde que explícitamente dividimos a la mitad los ladrillos en la segunda fila del mosaico de la plantilla), debemos usar un negativo x
valor en la segunda fila, luego asegúrese de que el último ladrillo se desborde del mosaico para esta conexión perfecta entre los ladrillos.Sin embargo, son buenos porque todo lo que cae fuera del patrón de mosaico se corta automáticamente.
¿Puedes identificar algunas secuencias repetitivas que se pueden grabar de manera más eficiente? Trabajemos más en ellos.
Reescribir en la carretera (-54B, total 100B)
<path>
es probablemente el elemento más poderoso en SVG. Puede dibujar casi cualquier forma con "comandos" en ella d
Hay 20 comandos disponibles, pero solo necesitamos los más simples para los rectángulos.
Aquí es de donde vengo:
<path d="M0 0h100v30h-100z
M110 0h100v30h-100
M0 40h45v30h-45z
M55 40h100v30h-100z
M165 40h55v30h-55z"/>
¡Lo sé, números y letras súper raros! Todos importan, por supuesto. Esto es lo que sucede en este caso particular:
M{x} {y}
: Se mueve a un punto basado en coordenadas.z
: Cierra el segmento actual.h{x}
: Dibuja una línea horizontal desde el punto actual con la longitud dex
en la dirección determinada por el signo dex
Letras minusculasx
indica coordenada relativa.v{y}
: Dibuja una línea vertical desde el punto actual con la longitud dey
en la dirección determinada por el signo dey
Letras minusculasy
indica coordenada relativa.
Esta marca es mucho más corta que la anterior (los saltos de línea y las sangrías son solo para facilitar la lectura). Y, oye, logramos reducir la mitad del tamaño original, hasta 100 bytes. Aún así, algo me hace sentir que podría ser menos...
Revisión de mosaicos (-38B, total 62B)
¿Nuestros mosaicos de plantilla no son partes repetidas? Está claro que en la primera fila se repite un ladrillo entero, pero ¿y en la segunda fila? Es un poco más difícil de ver, pero si cortamos el ladrillo del medio por la mitad, se vuelve obvio.
Bueno, el ladrillo del medio no está exactamente cortado por la mitad. Hay un ligero cambio, porque tenemos que tener en cuenta la brecha. De todos modos, acabamos de encontrar una plantilla más simple en la placa base, ¡lo que significa menos bytes! Esto también significa que tienes que cortar por la mitad width
de la nuestra <pattern>
elemento de 220 a 110.
<pattern id="p" width="110" height="80" patternUnits="userSpaceOnUse">
<!-- pattern content here -->
</pattern>
Ahora veamos cómo dibujar un mosaico simple. <path>
:
<path d="M0 0h100v30h-100z
M0 40h45v30h-45z
M55 40h55v30h-55z"/>
El tamaño se ha reducido a 62 bytes, que ahora es menos de un tercio del tamaño original, pero ¿por qué detenerse aquí cuando podemos hacer aún más?
Comandos de acortamiento de carreteras (-9B, total 53B)
Vale la pena profundizar un poco más <path>
elemento porque proporciona más consejos para optimizar los modelos SVG. Un concepto erróneo que tuve al trabajar con <path>
se trata de cómo fill
el atributo funciona. Después de jugar mucho con MS Paint cuando era niño, aprendí que cualquier forma que quiera rellenar con un color sólido debe estar cerrada, es decir. sin puntos abiertos. De lo contrario, la pintura se saldrá del molde y se derramará todo.
En SVG, sin embargo, esto no es cierto. Permítanme citar especificación yo mismo:
La operación de relleno rellena subtrayectos abiertos, realizando la operación de llenado como si se hubiera agregado un comando adicional de "cerrartrayecto" al trayecto para conectar el último punto del subtrayecto con el primer punto del subtrayecto.
Esto significa que podemos saltarnos comandos para cerrar la carretera (z
), ya que se considera que los subtrayectos se cierran automáticamente al rellenarlos.
Otra cosa útil que debe saber acerca de los comandos de ruta es que están disponibles en versiones en mayúsculas y minúsculas. Las letras minúsculas significan que se usan coordenadas relativas y las letras mayúsculas significan que se usan coordenadas absolutas en su lugar.
Es un poco más complicado que eso con H
y V
comandos porque incluyen sólo una coordenada. Así es como describiría estos dos comandos:
H{x}
: Dibuja una línea horizontal desde el punto actual hasta la coordenadax
.V{y}
: Dibuja una línea vertical desde el punto actual hasta la coordenaday
.
Cuando pintamos el primer ladrillo en el mosaico patrón, comenzamos desde (0,0)
Luego dibuja una línea horizontal para (100,0)
y una línea vertical para (100,30)
y finalmente dibuja una línea horizontal para (0,30)
Nosotros usamos h-100
comando en la última línea, pero es equivalente a H0
que es de dos bytes en lugar de cinco. Podemos reemplazar dos eventos similares y borrar nuestro código <path>
a esto:
<path d="M0 0h100v30H0
M0 40h45v30H0
M55 40h55v30H55"/>
9 bytes más cortados: ¿cuántos más pequeños podemos hacer?
Superación (-5B, total 48B)
Los comandos más largos que nos impiden obtener un modelo SVG totalmente optimizado son los comandos "mover a", que ocupan 4, 5 y 6 bytes, respectivamente. Una limitación que tenemos es que:
Un segmento de datos de ruta (si lo hay) debe comenzar con el comando "mover".
Pero todo está bien. El primero es el más corto de todos modos. Si cambiamos las filas, podemos llegar a una definición del camino en el que solo tenemos que movernos horizontal o verticalmente entre los ladrillos. ¿Qué pasa si podemos usar h
y v
comandos allí en su lugar M
?
El diagrama de arriba muestra cómo se pueden dibujar las tres figuras de una sola vez. Tenga en cuenta que usamos el hecho de que fill
operación cierra automáticamente la parte abierta entre (110,0)
y (0,0)
. Con esta reorganización, también movimos el espacio a la izquierda de un ladrillo de ancho completo en la segunda fila. Así es como se ve el código, aún dividido en un bloque en una fila:
<path d="M0 0v30h50V0
h10v30h50
v10H10v30h100V0"/>
Sin duda, hemos encontrado la solución más pequeña ahora que tenemos hasta 48 bytes, ¿no es así? MI…
Cortar números (-4B, total 44B)
Si puede ser un poco flexible con las dimensiones, hay otra pequeña forma de optimizar las plantillas SVG. Trabajamos con un ancho de ladrillo de 100
píxeles, pero estos son tres bytes 90
significa un byte menos cuando necesitamos escribirlo. De la misma manera usamos un espacio de 10
píxeles, pero si lo cambiamos a 8
en cambio, guardamos un byte para cada uno de estos eventos.
<path d="M0 0v30h45V0
h8v30h45
v8H8v30h90V0"/>
Por supuesto, esto también significa que tenemos que ajustar las dimensiones de la plantilla en consecuencia. Aquí está el código SVG optimizado final de la plantilla:
<pattern id="p" width="98" height="76" patternUnits="userSpaceOnUse">
<path d="M0 0v30h45V0h8v30h45v8H8v30h90V0"/>
</pattern>
La segunda línea en el fragmento superior, sin contar las depresiones, es 44 bytesHemos llegado hasta aquí en 197 bytes en seis iteraciones. esto se acumula 77,7% de reducción de tamaño!!
Pero me pregunto... ¿es este realmente el tamaño más pequeño posible? ¿Hemos considerado todas las formas posibles de optimizar los modelos SVG?
Lo invito a intentar minimizar aún más este código o incluso experimentar con métodos alternativos para optimizar los modelos SVG. ¡Me gustaría ver si podemos encontrar el verdadero mínimo global con la sabiduría de la multitud!
Más información sobre la creación y optimización de modelos SVG
Si está interesado en obtener más información sobre la creación y optimización de plantillas SVG, lea mi artículo sobre la creación de plantillas con filtros SVG. O, si desea ver una galería de más de 60 plantillas, puede ver Colección CodePen de PetitePatternsFinalmente, puedes ver mis lecciones en youtube para ayudarlo a profundizar aún más en los modelos SVG.
Deja una respuesta