Recentemente, criei um padrão de parede de tijolos como parte do meu #PetitePadrões series, um desafio onde crio padrões ou texturas de aparência orgânica em SVG dentro de 560 bytes (ou aproximadamente o tamanho de dois tweets). Para atender a essa restrição, passei por uma jornada que me ensinou algumas maneiras radicais de otimizar padrões SVG para que eles contenham o mínimo de código possível sem afetar a qualidade geral da imagem.
Quero orientá-lo no processo e mostrar como podemos usar um padrão SVG que começa em 197 bytes até meros 44 bytes - uma redução impressionante de 77.7%!
O padrão SVG
Isso é o que chamamos de padrão de tijolos “corrente”. É o padrão de tijolos mais comum por aí, e um que você certamente já viu antes: cada fileira de tijolos é compensada pela metade do comprimento de um tijolo, criando um padrão escalonado repetido. O arranjo é bem simples, tornando os SVGs <pattern>
elemento um ajuste perfeito para reproduzi-lo em código.
O SVG <pattern>
elemento usa um objeto gráfico pré-definido que pode ser replicado (ou “lado a lado”) em intervalos fixos ao longo dos eixos horizontal e vertical. Essencialmente, definimos um padrão de ladrilho retangular e ele é repetido para pintar a área de preenchimento.
Primeiro, vamos definir as dimensões de um tijolo e o espaço entre cada tijolo. Para simplificar, vamos usar números redondos e limpos: uma largura de 100
e uma altura de 30
para o tijolo e 10
para as folgas horizontais e verticais entre eles.
Em seguida, temos que identificar nosso tile “base”. E por “ladrilho” estou falando de ladrilhos de padrão ao invés de ladrilhos físicos, não confundir com os tijolos. Vamos usar a parte destacada da imagem acima como nosso padrão de ladrilho: dois tijolos inteiros na primeira linha e um inteiro ensanduichado entre dois meios tijolos na segunda linha. Observe como e onde as lacunas são incluídas, porque elas precisam ser incluídas no bloco de padrão repetido.
Ao usar <pattern>
, temos que definir o padrão width
e height
, que correspondem à largura e altura do ladrilho de base. Para obter as dimensões, precisamos de um pouco 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
Tudo bem, então nosso bloco padrão é 220✕80
. Também temos que definir o patternUnits
atributo, onde o valor userSpaceOnUse
essencialmente significa pixels. Por fim, adicionando um id
ao padrão é necessário para que possa ser referenciado quando estivermos pintando outro elemento com ele.
<pattern id="p" width="220" height="80" patternUnits="userSpaceOnUse"> <!-- pattern content here -->
</pattern>
Agora que estabelecemos as dimensões do tile, o desafio é criar o código para o tile de forma que renderize o gráfico com o menor número de bytes possível. Isto é o que esperamos terminar com o final:
Marcação inicial (197 bytes)
A abordagem mais simples e declarativa para recriar esse padrão que me vem à mente é desenhar cinco retângulos. Por padrão, o fill
de um elemento SVG é preto e o stroke
é transparente. Isso funciona bem para otimizar padrões SVG, pois não precisamos declará-los explicitamente no código.
Cada linha no código abaixo define um retângulo. O width
e height
são sempre definidos, e o x
e y
as posições só são definidas se um retângulo estiver deslocado do 0
posição.
<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"/>
A fileira superior do ladrilho continha dois tijolos de largura total, o segundo tijolo é posicionado para x="110"
permitindo 10
pixels de lacuna antes do tijolo. Da mesma forma há 10
pixels de lacuna depois, porque o tijolo termina em 210
píxeis (110 + 100 = 210
) no eixo horizontal, embora o <pattern>
largura é 220
píxeis. Precisamos desse pouco de espaço extra; caso contrário, o segundo tijolo se fundiria com o primeiro tijolo do ladrilho adjacente.
Os tijolos na segunda linha (inferior) são deslocados para que a linha contenha dois meios tijolos e um tijolo inteiro. Nesse caso, queremos que os tijolos de meia largura sejam mesclados para que não haja lacunas no início ou no final, permitindo que eles fluam perfeitamente com os tijolos em ladrilhos de padrão adjacentes. Ao compensar esses tijolos, também temos que incluir meias fendas, assim o x
valores são 55
e 165
, Respectivamente.
Reutilização de elemento, (-43B, total de 154B)
Parece ineficiente definir cada tijolo tão explicitamente. Não existe alguma maneira de otimizar os padrões SVG reutilizando as formas?
Eu não acho que seja amplamente conhecido que o SVG tem um <use>
elemento. Você pode referenciar outro elemento com ele e renderizar esse elemento referenciado onde quer que <use>
é usado. Isso economiza alguns bytes porque podemos omitir a especificação das larguras e alturas de cada tijolo, exceto o primeiro.
Dito isto, <use>
vem com um pequeno preço. Ou seja, devemos adicionar um id
para o elemento que queremos reutilizar.
<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"/>
O mais curto id
possível é um caractere, então escolhi “b” para tijolo. O <use>
elemento pode ser posicionado de forma semelhante a <rect>
, Com o x
e y
atributos como deslocamentos. Como cada tijolo tem largura total agora que mudamos para <use>
(lembre-se, nós explicitamente dividimos pela metade os tijolos na segunda linha do ladrilho padrão), temos que usar um negativo x
valor na segunda linha e, em seguida, certifique-se de que o último tijolo transborda do bloco para essa conexão perfeita entre os tijolos. No entanto, isso é bom, porque qualquer coisa que caia fora do ladrilho de padrão é automaticamente cortada.
Você consegue identificar algumas strings repetidas que podem ser escritas com mais eficiência? Vamos trabalhar nesses próximos.
Reescrevendo no caminho (-54B, total de 100B)
<path>
é provavelmente o elemento mais poderoso em SVG. Você pode desenhar praticamente qualquer forma com “comandos” em seu d
atributo. Existem 20 comandos disponíveis, mas só precisamos dos mais simples para retângulos.
Aqui é onde eu pousei com isso:
<path d="M0 0h100v30h-100z M110 0h100v30h-100 M0 40h45v30h-45z M55 40h100v30h-100z M165 40h55v30h-55z"/>
Eu sei, números e letras super estranhos! Todos eles têm significado, claro. Veja o que está acontecendo neste caso específico:
M{x} {y}
: Move para um ponto baseado em coordenadas.z
: Fecha o segmento atual.h{x}
: Desenha uma linha horizontal a partir do ponto atual, com o comprimento dex
na direção definida pelo sinal dex
. Minúsculasx
indica uma coordenada relativa.v{y}
: Desenha uma linha vertical a partir do ponto atual, com o comprimento dey
na direção definida pelo sinal dey
. Minúsculasy
indica uma coordenada relativa.
Essa marcação é muito mais concisa do que a anterior (quebras de linha e espaços em branco de recuo são apenas para facilitar a leitura). E, ei, conseguimos cortar metade do tamanho inicial, chegando a 100 bytes. Ainda assim, algo me faz sentir que isso poderia ser menor…
Revisão do bloco (-38B, total de 62B)
Nosso bloco de padrão não tem partes repetidas? É claro que na primeira linha um tijolo inteiro é repetido, mas e a segunda linha? É um pouco mais difícil de ver, mas se cortarmos o tijolo do meio ao meio, fica óbvio.
Bem, o tijolo do meio não é exatamente cortado ao meio. Há um pequeno deslocamento porque também temos que levar em conta a diferença. De qualquer forma, acabamos de encontrar um padrão de bloco de base mais simples, o que significa menos bytes! Isso também significa que temos que reduzir pela metade o width
do nosso <pattern>
elemento de 220 a 110.
<pattern id="p" width="110" height="80" patternUnits="userSpaceOnUse"> <!-- pattern content here -->
</pattern>
Agora vamos ver como o bloco simplificado é desenhado com <path>
:
<path d="M0 0h100v30h-100z M0 40h45v30h-45z M55 40h55v30h-55z"/>
O tamanho é reduzido para 62 bytes, o que já é menos de um terço do tamanho original! Mas por que parar aqui quando há ainda mais que podemos fazer!
Comandos de caminho de encurtamento (-9B, total de 53B)
Vale a pena aprofundar um pouco mais <path>
porque fornece mais dicas para otimizar os padrões SVG. Um equívoco que tive ao trabalhar com <path>
é sobre como o fill
atributo funciona. Tendo brincado muito com o MS Paint na minha infância, aprendi que qualquer forma que eu queira preencher com uma cor sólida deve ser fechada, ou seja, não ter pontos abertos. Caso contrário, a tinta vazará da forma e se espalhará por tudo.
No SVG, no entanto, isso não é verdade. Deixe-me citar a especificação si:
A operação de preenchimento preenche subcaminhos abertos executando a operação de preenchimento como se um comando adicional “closepath” fosse adicionado ao caminho para conectar o último ponto do subcaminho com o primeiro ponto do subcaminho.
Isso significa que podemos omitir os comandos close path (z
), pois os subcaminhos são considerados automaticamente fechados quando preenchidos.
Outra coisa útil a saber sobre os comandos de caminho é que eles vêm em variações de maiúsculas e minúsculas. Letras minúsculas significam que as coordenadas relativas são usadas; letras maiúsculas significam que são usadas coordenadas absolutas.
É um pouco mais complicado do que isso com o H
e V
comandos porque eles incluem apenas uma coordenada. Aqui está como eu descreveria esses dois comandos:
H{x}
: Desenha uma linha horizontal do ponto atual para coordenarx
.V{y}
: Desenha uma linha vertical do ponto atual para coordenary
.
Quando estamos desenhando o primeiro tijolo no ladrilho padrão, começamos do (0,0)
coordenadas. Em seguida, traçamos uma linha horizontal para (100,0)
e uma linha vertical para (100,30)
e, finalmente, desenhe uma linha horizontal para (0,30)
. Nós usamos o h-100
comando na última linha, mas é o equivalente a H0
, que é dois bytes em vez de cinco. Podemos substituir duas ocorrências semelhantes e aparar o código do nosso <path>
até isso:
<path d="M0 0h100v30H0 M0 40h45v30H0 M55 40h55v30H55"/>
Outros 9 bytes raspados - quanto menor podemos ir?
Ponte (-5B, total de 48B)
Os comandos mais longos que atrapalham um padrão SVG totalmente otimizado são os comandos “mover para” que ocupam 4, 5 e 6 bytes, respectivamente. Uma restrição que temos é que:
Um segmento de dados de caminho (se houver) deve começar com um comando “moveto”.
Mas está tudo bem. O primeiro é o mais curto de qualquer maneira. Se trocarmos as linhas, podemos chegar a uma definição de caminho onde só temos que nos mover horizontalmente ou verticalmente entre os tijolos. E se pudéssemos usar o h
e v
comandos lá em vez de M
?
O diagrama acima mostra como as três formas podem ser desenhadas com um único caminho. Observe que estamos aproveitando o fato de que o fill
operação fecha automaticamente a parte aberta entre (110,0)
e (0,0)
. Com esse rearranjo, também movemos a lacuna para a esquerda do tijolo de largura total na segunda linha. Veja como o código se parece, ainda dividido em um tijolo por linha:
<path d="M0 0v30h50V0 h10v30h50 v10H10v30h100V0"/>
Certamente, encontramos a menor solução absoluta agora que estamos com 48 bytes, certo?! Nós vamos…
Corte de dígitos (-4B, total de 44B)
Se você puder ser um pouco flexível com as dimensões, há outra pequena maneira de otimizar os padrões SVG. Estamos trabalhando com uma largura de tijolo de 100
pixels, mas são três bytes. Mudando para 90
significa um byte a menos sempre que precisarmos escrevê-lo. Da mesma forma, usamos um intervalo de 10
pixels — mas se mudarmos para 8
em vez disso, salvamos um byte em cada uma dessas ocorrências.
<path d="M0 0v30h45V0 h8v30h45 v8H8v30h90V0"/>
Claro, isso também significa que temos que ajustar as dimensões do padrão de acordo. Aqui está o código final do padrão SVG otimizado:
<pattern id="p" width="98" height="76" patternUnits="userSpaceOnUse"> <path d="M0 0v30h45V0h8v30h45v8H8v30h90V0"/>
</pattern>
A segunda linha no trecho acima — sem contar os recuos — é 44 bytes. Chegamos aqui de 197 bytes em seis iterações. Isso é um grosso 77.7% de redução de tamanho!
Eu estou querendo saber… este é realmente o menor tamanho possível? Analisamos todas as maneiras possíveis de otimizar os padrões SVG?
Convido você a tentar minificar ainda mais este código, ou até mesmo experimentar métodos alternativos para otimizar padrões SVG. Eu adoraria ver se poderíamos encontrar o verdadeiro mínimo global com a sabedoria da multidão!
Mais sobre como criar e otimizar padrões SVG
Se você estiver interessado em saber mais sobre como criar e otimizar padrões SVG, leia meu artigo sobre criando padrões com filtros SVG. Ou, se você quiser conferir uma galeria de mais de 60 padrões, você pode ver o Coleção PetitePatterns CodePen. Por último, você está convidado a assistir meus tutoriais no YouTube para ajudá-lo a se aprofundar ainda mais nos padrões SVG.
Otimizando padrões SVG para seu menor tamanho publicado originalmente em Truques de CSS. Você deve receba o boletim informativo.
- "
- 10
- 100
- 77
- 9
- 98
- Sobre
- absoluto
- Conta
- Adicional
- Todos os Produtos
- Permitindo
- já
- Outro
- abordagem
- ÁREA
- artigo
- atributos
- disponível
- MACHADOS
- Pouco
- Preto
- desafiar
- alterar
- fechado
- código
- comum
- da conexão
- contém
- conteúdo
- coordenar
- poderia
- Criar
- Atual
- dados,
- mais profunda
- down
- termina
- estabelecido
- tudo
- exemplo
- Exceto
- experimentar
- Finalmente
- Primeiro nome
- caber
- fluxo
- encontrado
- mais distante
- lacuna
- obtendo
- Global
- ter
- altura
- ajudar
- SUA PARTICIPAÇÃO FAZ A DIFERENÇA
- Destaque
- Como funciona o dobrador de carta de canal
- HTTPS
- identificar
- imagem
- incluir
- incluído
- IT
- se
- conhecido
- vazar
- APRENDER
- aprendido
- aproveitando
- Line
- pequeno
- olhou
- gosta,
- FAZ
- Fazendo
- gerenciados
- matemática
- mente
- mais
- a maioria
- mover
- MS
- número
- números
- compensar
- OK
- aberto
- otimizado
- de outra forma
- padrão
- físico
- ponto
- posicionado
- possível
- poderoso
- bastante
- preço
- processo
- fornece
- qualidade
- volta
- corrida
- Dito
- desatado
- Série
- conjunto
- formas
- semelhante
- simples
- SIX
- Tamanho
- So
- solução
- algo
- Espaço
- Spot
- começo
- começa
- Suportado
- falando
- Através da
- topo
- transparente
- tutoriais
- usar
- valor
- Ver
- W3
- Assistir
- boas-vindas
- O Quê
- dentro
- sem
- Atividades:
- trabalhar
- trabalho
- Equivalente há
- Youtube