SVG com media queries: imagens vetoriais responsivas em CSS

SVG com media queries: imagens vetoriais responsivas em CSS

Nos documentos HTML podemos mostrar, esconder ou reorganizar partes da página consoante as condições do viewport. Se a janela do browser tiver 480 pixels de largura, por exemplo, podemos passar a navegação de uma lista horizontal para uma lista vertical e recolhível. Com SVG e media queries podemos fazer algo parecido.

Este artigo é um excerto do livro de Tiffany, CSS Master, 3.ª edição. Dá-lhe uma vista de olhos se quiseres dominar outras funcionalidades avançadas de CSS3 como o Flexbox e a Grid!

Além de o usarmos com HTML, o CSS também se usa com SVG, ou Scalable Vector Graphics. O SVG é um formato de marcação para descrever imagens planas e bidimensionais. Como é uma linguagem de marcação, tem um document object model e pode ser trabalhado com CSS.

Com CSS podemos mudar o aspeto de um SVG consoante a interação do utilizador. Também podemos usar o mesmo documento SVG em vários sítios e mostrar ou esconder partes dele consoante a largura do viewport.

Todos os principais motores de browser suportam SVG 1.1 há anos. O suporte das funcionalidades de SVG 2, esse, ainda está em desenvolvimento. À data em que isto foi escrito, o suporte dos browsers é limitado para alguns dos temas aqui abordados, e pode ter mudado quando leres estas linhas. Fica atento à meta-issue do Chromium Implement SVG2 features, que segue os trabalhos nos browsers baseados em Chromium. Para o Firefox, vê a meta-issue Support SVG 2 features, para o Safari, a meta-issue do WebKit Implement SVG 2. Andar pelos issue trackers não é agradável, mas por agora é a melhor forma de seguir o suporte do SVG 2.

Antes de avançarmos, porém, vejamos o que é o SVG e porque vale a pena usá-lo.

Imagens vetoriais e imagens raster

A maior parte das imagens hoje usadas na web são imagens raster, também chamadas bitmap. As imagens raster são feitas de pixels numa grelha fixa, com um número definido de pixels por polegada. JPEG, WebP, GIF e PNG são todos exemplos de imagens raster.

As imagens raster dependem da resolução. Uma imagem PNG a 144 PPI (pixels por polegada) fica impecável num dispositivo com um ecrã de 144 PPI. Num ecrã de resolução mais alta (400 PPI), porém, essa mesma imagem pode ficar desfocada. As imagens raster têm também dimensões fixas e ficam melhor no tamanho original: passar uma imagem de 150 por 150 pixels para 300 por 300 pixels deforma-a.

Em vez de pixels numa grelha, os formatos vetoriais descrevem as formas primitivas – círculos, retângulos, linhas ou paths – que compõem uma imagem, e a posição de cada uma no sistema de coordenadas do documento. As imagens vetoriais são, por isso, independentes da resolução e mantêm a qualidade seja qual for a resolução ou a dimensão do ecrã.

A independência da resolução é a maior vantagem do SVG. Podemos ampliar ou reduzir as imagens sem perda de qualidade, e o mesmo ficheiro fica bem em dispositivos de PPI alto e baixo. Dito isto, o SVG é pouco adequado à quantidade de dados de cor que uma fotografia exige: dá-se muito melhor com desenhos e formas. Usa-o em vez de imagens PNG e GIF, e como substituto mais flexível das icon fonts.

Outra vantagem do SVG é ter sido pensado para trabalhar com as restantes linguagens da web. Podemos criar, alterar e manipular imagens SVG com JavaScript. Ou, como veremos mais à frente, aplicar-lhes estilos e animações com CSS.

Associar CSS a documentos SVG

Usar CSS com SVG parece-se muito com usá-lo com HTML. Podemos aplicar o CSS pelo atributo style de um elemento SVG, reunir o CSS do documento num elemento <style> ou ligar uma folha de estilos externa. As vantagens e os inconvenientes de cada método são os mesmos que no HTML.

Usar o atributo style

Eis um documento SVG simples, cujo código desenha um círculo preto:

svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"
➥enable-background="new 0 0 200 200">
<circle cx="101.3" cy="96.8" r="79.6" />
</svg>

A imagem abaixo mostra como este código é renderizado num browser.

Um círculo em SVG

Vamos dar ao nosso círculo um preenchimento cor-de-rosa com CSS, pelo atributo style:

svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200"
➥enable-background="new 0 0 200 200">
<circle cx="101.3" cy="96.8" r="79.6" style="fill: #f9f" />
</svg>

O efeito é o que se vê abaixo.

Usar o atributo style para adicionar uma cor de preenchimento

Eis uma diferença entre usar CSS com HTML e usá-lo com SVG: os nomes das propriedades. Muitas propriedades CSS que usamos nos documentos HTML não funcionam com SVG, e vice-versa. Voltamos a isto mais à frente no capítulo.

Usar o atributo style não é, claro, a melhor forma de trabalhar com CSS: limita a reutilização desses estilos em vários elementos ou documentos. Mais vale CSS incorporado ou ligado.

Incorporar CSS nos documentos SVG

Em vez do atributo style, podemos usar o elemento <style>:

svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0
➥ 200 200" enable-background="new 0 0 200 200">
<style type="text/css">
circle {
fill: #0c0;
}
</style>
<circle cx="101.3" cy="96.8" r="79.6" />
</svg>

Incorporar o CSS num documento SVG permite reutilizar esses estilos em vários elementos do mesmo documento, mas impede a partilha entre documentos diferentes. Para logótipos e ícones serve na perfeição. Se estás a construir algo como uma biblioteca de estilos para gráficos, é preferível um ficheiro CSS externo.

Com um editor de texto normal também podes acrescentar CSS a imagens SVG criadas em programas como o Sketch, o Inkscape ou o Illustrator. Isso não te impede de continuar a editar a imagem na aplicação de desenho, mas se guardares o ficheiro a partir de um software de imagem a aplicação pode reescrever ou apagar o teu CSS.

Ligar um SVG a um ficheiro CSS externo

Tal como no HTML, ligar um ficheiro CSS externo permite partilhar estilos entre vários documentos SVG. Para isso, acrescenta <? xml-stylesheet ?> ao início do teu ficheiro SVG:

svg
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://www.gekkode.com/pt/style.css" type="text/css"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0
➥ 200 200" enable-background="new 0 0 200 200">
<circle cx="101.3" cy="96.8" r="79.6" />
</svg>

Usar o elemento <link>

Também podes usar o elemento HTML <link>. Com este método tens de incluir o atributo xmlns, como se vê abaixo:

svg
<link href="style.css" type="text/css" rel="stylesheet"
 xmlns="http://www.w3.org/1999/xhtml" />

Nota: alguns browsers mais antigos exigem que o elemento <link> esteja envolvido em tags <defs> ou <g>.

O <link> não é um elemento SVG: pertence ao HTML e ao XHTML. O XHTML é uma variante do HTML analisada segundo as regras da marcação XML. Com essas regras podemos ir buscar elementos e comportamentos a outros dialetos XML, como o XHTML. Para isso, porém, temos de dizer ao browser a que namespace pertence o elemento, através do atributo xmlns.

Usar @import

Também podemos ligar uma folha de estilos externa com @import, dentro das tags <style> e </style>:

markup
<style type="text/css">
@import("https://www.gekkode.com/style.css");
</style>

Este método funciona de forma semelhante ao do <link>.

SVG e o elemento <img>: os limites

Ligar um ficheiro SVG a recursos externos, ficheiros CSS incluídos, não funciona com o elemento <img>. É uma limitação de segurança do <img>, integrada nos browsers.

Se quiseres usar CSS ligado com as tuas imagens SVG, tens de fazer uma de duas coisas:

  • usar um elemento <style> no documento SVG para pôr o CSS em linha,
  • usar um elemento <iframe> ou <object> (ver a nota abaixo).

Em geral, é preferível <iframe> a <object>. O <object>, no entanto, pode ser filho de um elemento <a>, ao passo que o <iframe> não pode. Usar <iframe> ou <object> torna também a árvore do documento SVG acessível à árvore do documento pai: podemos interagir com ela em JavaScript (por exemplo, com document.querySelector('iframe').contentDocument).

SVG em linha e recursos externos

Quando acrescentas SVG ao HTML, o browser não carrega os recursos externos referenciados pelo documento SVG. Podemos, ainda assim, ligar o CSS do nosso SVG a partir do <head> do documento HTML:

markup
<head><link href="svg.css" type="text/css" rel="stylesheet" />
</head>

Os elementos SVG dentro de documentos HTML fazem também parte da árvore do documento HTML. Com SVG em linha, é perfeitamente possível juntar as regras de estilo do HTML e do SVG na mesma folha de estilos.

Diferenças entre SVG e HTML

Embora SVG e HTML sejam ambos linguagens de marcação, há entre eles duas diferenças de peso que mudam a forma como trabalham com CSS:

  • o SVG não segue o box model do CSS,
  • o SVG não tem um esquema de posicionamento.

O SVG não segue o box model do CSS

Com HTML, o layout CSS segue as regras do box model. O SVG, pelo contrário, usa coordenadas para dispor os elementos: segue aquilo que se percebe melhor como um «modelo de formas».

As formas SVG não se limitam a caixas retangulares. Por isso, a maior parte das propriedades ligadas ao box model não se aplica aos elementos SVG. Não podes, por exemplo, mudar o padding ou a margin de um elemento SVG. Também não podes usar as propriedades box-sizing, box-shadow, outline ou border-*. O layout em grelha, os floats e o Flexbox ficam igualmente de fora.

Podes, isso sim, usar CSS para definir ou alterar uma série de propriedades e de valores de atributos SVG. A lista completa está na especificação SVG 2, ainda que na maior parte dos browsers o suporte seja incompleto. Algumas propriedades CSS, como filter, funcionam tanto com SVG como com HTML: vemos algumas neste capítulo, dentro de técnicas específicas.

O SVG não tem esquema de posicionamento

Quando o CSS é usado com HTML, as caixas dos elementos podem:

  • ficar no fluxo normal,
  • ser retiradas do fluxo normal com a propriedade float,
  • ser retiradas do fluxo normal com a propriedade position.

A especificação CSS chama-lhes esquemas de posicionamento. Em SVG esses esquemas não existem: a propriedade position não tem qualquer efeito sobre os elementos SVG, e o mesmo vale para propriedades como top, left e bottom, que dependem do posicionamento. Num documento SVG também não podes pôr elementos a flutuar.

Em vez disso, o SVG usa um sistema de coordenadas para colocar os elementos. Para desenhar um <circle>, por exemplo, defines as coordenadas do centro com os atributos cx e cy e o comprimento do raio com o atributo r. Um polígono é uma série de coordenadas de pontos com os segmentos traçados entre eles. Por outras palavras: podes decidir onde um elemento é desenhado no canvas SVG, mas não podes «posicioná-lo» no sentido CSS do termo.

A par dos esquemas de posicionamento, o SVG também não tem a noção de z-index nem contextos de empilhamento.

Nota: a especificação SVG 2 define um comportamento para o z-index e para os contextos de empilhamento nos documentos SVG, mas a maior parte dos browsers ainda não o suporta.

Os elementos SVG empilham-se pela ordem do código-fonte: os que aparecem mais à frente no documento ficam mais acima na pilha. Para mudar a ordem de empilhamento, tens de os deslocar no código ou reordená-los na árvore do DOM com JavaScript.

Na prática, a maior parte das propriedades do CSS 2.1 não se aplica aos documentos SVG. Fogem à regra as animações e as transformações, display, overflow, visibility, filter e algumas propriedades ligadas à fonte e ao texto. Em vez delas, terás de usar as propriedades de estilo específicas dos documentos SVG. A maior parte delas também pode ser expressa como atributo de um elemento SVG.

Aplicar estilos aos elementos SVG

Eis um exemplo simples de como dar estilo a elementos SVG com CSS. Comecemos pelo nosso documento SVG, um ficheiro autónomo:

svg
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://www.gekkode.com/pt/styles.css" type="text/css" ?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink=
➥"http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 497
➥ 184" enable-background="new 0 0 497 184" xml:space="preserve">
<polygon id="star" points="77,23.7 98.2,66.6 145.5,66.5 111.2,
➥106.9,119.3,154 77,131.8 34.7,154 42.8,106.9 8.5,67.5 55.8,
➥66.6 "/>
<circle id="circle" cx="245" cy="88.9" r="67.5"/>
</svg>

Esta marcação produz a imagem abaixo.

Uma imagem SVG simples com um círculo e uma estrela

Embora não possamos usar a maior parte das propriedades CSS com documentos SVG, podemos usar CSS para mudar a cor de um elemento. Vamos tornar a nossa estrela amarela:

css
#star {
fill: hsl( 44, 100%, 50% );
}

Vais ver muitas vezes o fill usado com tags SVG, por exemplo <circle fill="rgb( 255, 185, 0 )" cx="3" cy="10" r="100">, mas é também uma propriedade que podes usar em CSS.

Também podemos usar CSS para ajustar o stroke de um elemento, ou seja, o contorno de uma forma SVG. O contorno de uma forma existe mesmo quando nenhum stroke está definido. Vamos dar ao nosso círculo uma borda tracejada azul-escura com dez pixels de largura e definir a sua propriedade fill como cornflowerblue:

css
circle {
fill: cornflowerblue;
stroke: darkblue;
stroke-width: 10;
stroke-dasharray: 10, 15;
stroke-linecap: round;
}

Tudo junto, eis o resultado.

Uma imagem SVG simples com um círculo e uma estrela

Usar atributos SVG como propriedades CSS

Também podemos usar CSS para definir os valores das coordenadas de alguns elementos de forma: <rect>, <circle> e <ellipse>. Normalmente, para estes elementos usaríamos atributos SVG:

svg
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
<rect x="20" y="200" width="300" height="300" fill="#f60" />
</svg>

O SVG 2, porém, redefiniu alguns atributos SVG como propriedades geométricas. Ou seja, podemos usar CSS para lhes dar valores:

css
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
    <style type="text/css">
      rect {
x: 20px;
y: 50px;
width:  300px;
height: 300px;
fill: #f60;
}
    </style>
    <rect />
</svg>

As propriedades de coordenada (x e y), as das coordenadas do centro (cx e cy) e as do raio (rx, ry e r) podem ser definidas em CSS. O mesmo vale para width e height. Nos atributos SVG as unidades são facultativas, os valores CSS, pelo contrário, exigem uma unidade. Comprimentos e percentagens são ambos válidos para as propriedades aqui referidas, mas atenção: nos documentos SVG os comprimentos funcionam de forma um pouco diferente. Lembra-te de que o S de SVG quer dizer scalable. O tamanho calculado de um elemento SVG depende também de:

  • a width e a height calculadas do elemento SVG raiz
  • o valor do atributo viewBox no elemento raiz
  • qualquer transformação de escala aplicada ao elemento ou aos seus ascendentes.

Por outras palavras, os cantos do nosso elemento <rect> estão em (20, 50), (20, 320), (350, 320) e (20, 350) no sistema de coordenadas SVG. As dimensões reais, porém, podem ser maiores ou menores, consoante os fatores acima.

Nem todos os atributos SVG estão disponíveis via CSS – pelo menos não em todos os browsers. O Chrome e o Edge, por exemplo, suportam a função CSS path() para definir os dados do traçado, com a propriedade d:

css
path {
d: path("M 454.45223,559.21474 -304.96705,163.45948 417.4767,-296.33928 Z");
}

Até hoje são os únicos browsers que o fazem. O trabalho para acrescentar suporte no Firefox e no WebKit ainda nem começou.

Para os restantes elementos de forma, a especificação do SVG 2 é francamente incoerente. Por agora, tens de usar os atributos dos elementos para definir as propriedades de <line>, <polyline> e <polygon>.

Dito isto, não estamos obrigados a usar seletores de tipo (ou de elemento) para definir estas propriedades. Podíamos, por exemplo, definir círculos pequenos, médios e grandes com seletores de classe:

svg
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">

.small {
cx: 20px;
cy: 20px;
r:  20px;
fill:  #0c0;
}

.medium {
cx: 80px;
cy: 80px;
r:  60px;
fill:  #fc0;
}

.large {
cx: 220px;
cy: 220px;
r:  120px;
fill: #00f;
}

</style>

<circle class="small" />
<circle class="medium" />
<circle class="large" />
</svg>

Seja qual for o seletor, usar a sintaxe CSS para especificar estas propriedades torna-as também fáceis de animar. Vemos como na secção seguinte.

Animar e fazer transições das propriedades CSS do SVG

Usar CSS com SVG fica mais interessante quando juntamos transições e animações. O processo é o mesmo da animação de elementos HTML com CSS, mas com propriedades específicas do SVG. Vamos criar um efeito de estrela a cintilar, a partir deste documento SVG:

svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px"
 y="0px" viewBox="0 0 497 184" xml:space="preserve">
<defs>
<link href="twinkle.css" type="text/css" rel="stylesheet"
 xmlns="http://www.w3.org/1999/xhtml"/>
</defs>
<polygon class="star" points="77,23.7 98.2,66.6 145.5,66.5 111.2
➥,106.9 119.3,154 77,131.8 34.7,154 42.8,106.9 8.5,67.5
➥ 55.8,66.6 "/>
<polygon class="star twinkle" points="77,23.7 98.2,66.6 145.5,
➥66.5 111.2,106.9 119.3,154 77,131.8 34.7,154 42.8,106.9
➥ 8.5,67.5 55.8,66.6 "/>
</svg>

O nosso documento tem dois elementos polígono em forma de estrela, cada um com a classe star. Para obter o efeito de cintilação vamos animar o primeiro. Eis o nosso CSS:

css
@keyframes twinkle {
from {
fill-opacity: .4;
}
to {
fill-opacity: 0;
transform: scale( 2 );
}
}
.star {
fill: rgb( 255,195,0 );
transform-origin: 50% 50%;
}
.twinkle {
animation: twinkle 1.5s infinite forwards ease-in;
}

Usámos aqui a propriedade específica do SVG fill-opacity. Como em CSS em geral, se conseguirmos interpolar o valor de uma propriedade de estilo SVG, conseguimos animá-lo ou fazê-lo transitar. Na imagem abaixo vês dois momentos diferentes da animação.

A nossa animação da estrela a pulsar

Vejamos outro exemplo. Desta vez criamos um efeito de desenho, fazendo transitar a propriedade stroke-dasharray. Eis o nosso documento SVG:

svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
➥xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 200 200" enable-background="new 0 0 200 200">
<circle fill="transparent" stroke-width="16" cx="101.3"
 cy="96.8" r="79.6"/>
</svg>

O stroke-dasharray aceita uma lista de valores de comprimento ou de percentagem, separados por vírgulas, que criam um padrão tracejado. Os valores ímpares determinam o comprimento do traço, os pares, o comprimento do espaço. Um valor de stroke-dasharray de 5, 10 significa que o traço terá 5px de comprimento, com um espaço de 10px entre cada traço. Um valor de 5, 5, 10 alterna traços de 5px e de 10px com 5px de espaço entre eles.

Podemos usar o stroke-dasharray para criar um efeito de desenho, começando com um comprimento de traço nulo e um espaço grande, e acabando com um traço longo e sem espaço nenhum. Depois fazemos a transição entre os dois. Eis o nosso CSS:

css
circle {
transition: stroke-dasharray 1s ease-in;
fill: transparent;
stroke-dasharray: 0, 500;
}
.animate {
stroke-dasharray: 500, 0;
}

No início da transição o traço é invisível, porque o comprimento do traço é 0 e o espaço é 500. Mas quando acrescentamos a classe animate ao círculo, levamos o comprimento do traço até 500 e eliminamos o espaço. O efeito parece-se com desenhar um círculo com um compasso. Porquê 500? É o valor mais pequeno que produz exatamente este efeito.

Um futuro de paths animados

Lembras-te do exemplo em que definíamos um traçado via CSS, na secção anterior? Um dia talvez possamos animar paths em todos os browsers, com CSS:

css
path {
d: path("M357.5 451L506.889 192.25H208.111L357.5 451Z");
transition: d 1s ease-in-out;
}
.straighten {
d: path("M357.5 8871L406 -10113.75H208.111L357.5 351Z");
}

Por agora, porém, só os browsers baseados em Chromium, como o Google Chrome e o Microsoft Edge, suportam a animação das definições de path desta forma. Para que funcione noutros browsers, usa uma biblioteca JavaScript como a GreenSock e o seu MorphSVGPlugin. Além da compatibilidade com os browsers, a GreenSock e o MorphSVGPlugin tornam simples o morphing entre duas formas, seja qual for o número de pontos de cada uma.

Usar SVG com media queries

Nos documentos HTML podemos mostrar, esconder ou reorganizar partes da página consoante as condições do viewport. Se a janela do browser tiver 480 pixels de largura, por exemplo, podemos passar a navegação de uma lista horizontal para uma lista vertical e recolhível. Com media queries e documentos SVG podemos fazer algo parecido. Peguemos num logótipo, como o da empresa fictícia Hexagon Web Design & Development que se vê abaixo.

Um logótipo bem real para uma empresa fictícia

Sem media queries, este logótipo SVG limitar-se-ia a esticar ou a encolher para caber no viewport ou no seu contentor. Com media queries, podemos fazer coisas mais espertas.

Distingamos o viewport do documento HTML do viewport do documento SVG. Quando o SVG está em linha, os dois coincidem e o documento SVG comporta-se como qualquer outro elemento HTML. Quando um documento SVG é ligado – como acontece com os elementos <iframe>, <object> ou <img> –, estamos a lidar com o viewport do documento SVG.

As media queries funcionam nos dois casos, mas quando o documento SVG é ligado o seu viewport é independente do documento HTML. Nesse caso, o tamanho da janela do browser não determina o tamanho do viewport SVG: quem o determina são as dimensões do elemento <object>, <iframe> ou <img>. Peguemos no documento SVG (abreviado) a seguir:

svg
<svg version="1.1" id="HexagonLogo" xmlns="http://www.w3.org/2000/
➥svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 viewBox="0 0 555 174" xml:space="preserve">
<defs>
<style type="text/css">
/* CSS goes here */
</style>
</defs>
<g id="hex">
<polygon id="hexagonbg" points="55.2,162 10,86.5 55.2,11
➥ 145.5,11 190.7,86.5 145.5,162  "/>
<path id="letterH" fill="#FFFFFF" d="M58,35.5h33v35.2h18.
➥4V35.5 h33.2v103.4h-33.2v-38.3H91v38.3H58V35.5z M77.5,126.5V87.
➥3h45.6v39.2h4V47.9h-4v35.6H77.5V47.9h-4v78.6H77.5z"/>
</g>

<g id="word-mark">
<g id="hexagon-word">
            ...
</g>
<g id="web-design-and-dev">
            ...
</g>
</g>
</svg>

Nota: uma demonstração completa desta técnica, incluindo o código-fonte integral deste documento SVG, está disponível online.

Nos viewports mais pequenos, mostremos apenas o H dentro do símbolo hexagonal:

css
@media (max-width: 320px) {
[id=word-mark] {
display: none;
}
}

A partir de agora, sempre que o contentor do nosso SVG for menor ou igual a 20em, só a parte do símbolo do logótipo fica visível.

Mostrar e esconder elementos consoante o tamanho do viewport SVG

Para acionar esta vista a partir do documento HTML, define a largura do contentor SVG:

markup
<iframe src="hexlogo.svg" style="width: 320px; border:0"></iframe>

Como talvez tenhas reparado na imagem acima, a nossa imagem SVG mantém as dimensões intrínsecas mesmo com uma parte escondida. Infelizmente, é uma limitação do SVG. Para a contornar, temos de mexer no viewBox do documento SVG, mas apenas quando o viewport ficar abaixo de um certo tamanho. É um excelente caso de uso para o matchMedia.

O viewBox, como o nome indica, determina a área visível de um elemento SVG. Ajustando-o, decidimos que parte de uma imagem SVG preenche o viewport. Eis um exemplo que usa matchMedia e uma media query para atualizar o atributo viewBox:

javascript
<script type="text/javascript">
const svg = document.querySelector( 'svg' );

/* Guardar o valor original numa variável */
const originalViewBox = svg.getAttribute( 'viewBox' );

/* Definir a media query e o respetivo objeto */
const mq = matchMedia( '( max-width: 320px )' );

/* Definir o handler */
const updateViewBox = () {
if (mq.matches) {
/* Alterar as dimensões da viewBox para mostrar o hexágono */
        svg.setAttribute( 'viewBox', '0 0 200 174' );
} else {
        svg.setAttribute( 'viewBox', originalViewBox );
}
}

svg.addEventListener( 'SVGLoad', updateViewBox );

/* Dispara se a condição da media query mudar */
mq.addEventListener( 'change', updateViewBox );
</script>

A partir daqui, quando o contentor SVG for menor ou igual a 320 pixels, o valor do atributo viewBox será "0 0 200 174". Acima dos 320 pixels, o viewBox volta ao valor inicial.

Ajustar o atributo viewBox consoante a largura do viewport

Como esta técnica se apoia no atributo onload ou no evento SVGLoad, é boa ideia incorporar o CSS e o JavaScript no ficheiro SVG. Quando o CSS é externo, o SVGLoad pode disparar antes de a folha de estilos ligada acabar de carregar.

Usar media queries com background-size

Os documentos SVG e as media queries não se limitam às imagens em primeiro plano. Também podemos redimensionar o viewport SVG com a propriedade CSS background-size.

Vamos começar com este documento SVG:

svg
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
 xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 viewBox="-20 -20 250 250" xml:space="preserve">
<style type="text/css">
circle {
stroke: #000;
stroke-width: 30;
fill: #009688;
}
@media ( width: 100px ) {
circle {
fill: #673ab7;
}
}
@media ( width: 300px ) {
circle {
fill: #ffc107;
}
}
</style>
</defs>
<circle cx="100" cy="100" r="100" />
<circle cx="100" cy="100" r="50" />
</svg>

É um caso simples: os nossos elementos <circle> recebem um novo fill em larguras de viewport precisas. Por predefinição o fill é verde-azulado, a 100 pixels de largura passa a roxo, e a 300 pixels, a amarelo.

Para que isto funcione, temos de usar a nossa imagem SVG como imagem de fundo e definir o background-size do seletor. Aqui vamos usá-la como fundo do <body> e dos elementos <li>:

css
body, li  {
background: url(circles.svg);
}
body  {
background-color: #9c27b0;
background-size: 300px auto;
}
li {
background-position: left center;
background-repeat: no-repeat;
background-size: 1em auto;
padding-left: 40px;
font-size: 24px;
margin: 1rem 0;
}

O resultado é o que se vê abaixo.

Manipular o viewport SVG com a propriedade CSS background-size

Conclusão

Usar SVG com CSS dá-nos mais margem para documentos flexíveis e adaptáveis. A esta altura já deves saber como:

  • usar CSS para dar estilo a elementos SVG
  • animar propriedades SVG
  • usar as media queries do CSS e a API matchMedia para mostrar e esconder partes de um documento SVG.

CSSHTML

Damien Flandrin Programador web desde 2010, criador da Gekkode e do Email Impact. Cada artigo é testado num projeto real antes de ser publicado. Contacto
Newsletter

Os novos testes, tutoriais e projetos, por e-mail.

Testes reproduzíveis, código versionado, resultados datados. Nunca spam.