Você está na página 1de 11

Posicionamento Relativo Mimic Dentro de um SVG com SVGs agrupados

By Sara Soueidan

Posicionar elementos dentro de uma imagem SVG é muito semelhante - se não idêntico – a posicionar
elementos em HTML. Cada elemento em SVG está posicionado "absolutamente" em relação à janela
de exibição SVG, e a posição dentro da janela de exibição é regida pelo atual sistema de coordenadas em
uso. Mas essa semelhança em elementos de posicionamento não deve ocultar o fato de que há uma
diferença fundamental entre elementos SVG e elementos HTML: elementos SVG não têm um modelo
de caixa como os elementos HTML em CSS.

Revisão rápida do modelo Box em CSS

Cada elemento HTML tem um modelo de box em CSS que é composto por quatro caixas (boxes):
caixa de conteúdo (content), a caixa de "padding", a caixa de "border, e a caixa de "margin".

Normalmente, quando o tamanho de um elemento é definido, as propriedades largura (width) e altura


(height) determinam a largura e altura da caixa de conteúdo do elemento. Qualquer enchimento
(padding) adicionado ao elemento irá aumentar a largura e/ou a altura total do elemento, é assim que o
modelo de caixa padrão funciona no que diz respeito ao dimensionamento do elemento. A propriedade
de dimensionamento de caixa permite-lhe controlar a forma como o dimensionamento das dimensões
de um elemento funciona. Mais especificamente, utilizando a propriedade de dimensionamento de
caixa, você pode dizer ao navegador para incluir a largura de preenchimento e/ou largura da borda, na
largura do elemento, sem aumentar essa largura. Isso é útil para muitos casos de uso, mas
principalmente para quando você está construindo sistemas de grade em CSS, por exemplo.

O modelo de caixa (box model) de um elemento também é utilizado para criar um contexto de
posicionamento para o conteúdo do elemento, quando aplicável, ou para o próprio elemento.

Quando o valor da posição (position) de um elemento muda de valor estático (static) padrão, ou ele cria
um contexto de posicionamento para os seus descendentes ou para si. Sempre que a posição padrão
muda, num contexto de posicionamento, é necessário especificar onde e como um elemento vai ser
posicionado fora do fluxo de conteúdo da página padrão.
Se você quiser remover um elemento do fluxo de conteúdo da página, você pode fazer isso,
posicionando-o absolutamente. Posicionar um elemento absolutamente significa que ele será
posicionado em relação a um dos seus ascendentes, usando a caixa desse ascendente como um contexto
de posicionamento.

Cada contexto de posicionamento, no entanto, requer um sistema de coordenadas. O sistema de


coordenadas é estabelecido pelas dimensões (largura e altura) do modelo de caixa do elemento.
Qualquer descendente do elemento será, então, posicionado no interior e em relação ao elemento que
utiliza este sistema de coordenadas.

Em SVG, no entanto, há apenas um sistema de coordenadas usado por padrão para posicionar
elementos dentro da janela de exibição: o atual sistema de coordenadas em uso, estabelecido pela "SVG
viewBox". Assim, quando um elemento precisa ser posicionado dentro de um SVG, é posicionado em
relação a toda a janela de visualização SVG.

Tecnicamente, existem dois sistemas de coordenadas padrão em SVG, mas apenas um deles é relevante
quando se lida com posicionamento SVG, a menos que você altere explicitamente os valores de ambos.

Elementos individuais não têm um modelo de caixa e, portanto, não têm os seus próprios sistemas de
coordenadas que possam ser usadas como contextos de posicionamento para outros elementos. Então,
como posicionar um elemento SVG em relação a outro elemento ou grupo de elementos SVG? A
resposta é: <svg> aninhados.

Aninhando SVG's

Uma das minhas coisas favoritas sobre SVG é que é uma imagem definida pela marcação. E que a
marcação é o que nos dá um monte de poder sobre o conteúdo dessa imagem e como eles são exibidos.

Você pode aninhar SVG's. Ou seja, você pode colocar um <svg> dentro de outro <svg>. E então você
pode colocar uma outro <svg> dentro deste <svg>. E então você pode colocar mais um <svg> dentro
desse <svg>. E assim por diante.

Você pode aninhar SVG's tão profundamente quanto você quiser. A quantos níveis de profundidade
você quiser ir, depende do que você quer fazer e se você precisa ou não, é claro. Eu, pessoalmente, nunca
necessito aninhar SVGs por mais de dois níveis de profundidade.

Algumas notas sobre SVG's aninhados

O elemento interno <svg> não requer a especificação de um namespace (xmlns) sobre ele, porque ele é
assumido como sendo do mesmo namespace do <svg> externo. Mesmo o <svg> externo (root) não
requer um namespace se ele está incorporado em linha (inline) a um documento HTML5.

Você pode usar um SVG aninhado para agrupar elementos juntos e, em seguida, colocá-los dentro do
SVG "pai". Claro, você pode agrupar elementos dentro de um SVG usando a tag <g> (grupo), mas em
vez disso, usar um <svg> interno tem algumas vantagens, como ser capaz de especificar a largura e a
altura do grupo, e posicioná-lo usando valores absolutos x e y em vez de ter que usar transformações
(para <g>). Ao especificar uma largura e altura para o <svg>, você restringe o conteúdo dentro dele para
os limites da janela de exibição (viewport) que é definida pelos atributos de largura e altura. Qualquer
conteúdo que estiver além desses limites será cortado.
Valores percentuais previstos para os elementos de um <svg> no interior de outro serão calculados em
relação a este SVG, não ao SVG raiz. Valores percentuais previstos no <svg> em si, colocado no interior
de outro, serão calculados em relação ao SVG raíz.

Então, por quê aninhar SVG's?

Um caso de uso para SVG's aninhados está criando efeitos responsivos interessantes, onde o conteúdo
do SVG iria esconder ou revelar outras partes do conteúdo em diferentes tamanhos de janela.

Um exemplo é a ilustração a seguir, o SVG de um pequeno pássaro dentro de um ovo:

Normalmente, se o SVG é responsivo, o redimensionamento da tela faria todo o SVG menor, mantendo
as posições do conteúdo no interior do mesmo e as relações espaciais entre as mesmas:

Com elementos SVG's aninhados, podemos criar "camadas" separadas dentro da raiz <svg> que pode
então controlar de modo que o conteúdo dessas camadas mudem de posição dentro do SVG raiz com as
alterações de tamanho da janela. Ao fazer isso, podemos mostrar e ocultar partes diferentes do conteúdo
dentro do SVG como desejado.

Ao aninhar elementos SVG, podemos criar "camadas" separadas dentro do <svg> principal.

Por exemplo, podemos separar a ilustração acima em 3 camadas, que revelariam o pequeno pássaro, em
tamanhos menores:
Este efeito é conseguido através da utilização de diferentes valores para "preserveAspectRatio" em cada
um dos SVGs interiores. Isso garante que o conteúdo de cada SVG-i, o conteúdo de cada "camada",
"adere"(sticks) a qualquer das bordas do SVG raiz, revelando, assim, o conteúdo do meio.

Posicionamento relativo em SVG Usando um SVG aninhado

O fato de que o conteúdo de um SVG no interior de outro seja posicionado em relação a esse SVG, nos
leva um passo mais perto de elementos de posicionamento em relação a outros elementos em vez de ser
em relação ao SVG raiz.

Mas como é que um SVG aninhado nos permitirá posicionar um elemento em relação a outro elemento
não-SVG?

Antes de responder a essa pergunta, precisamos entender o que caixa delimitadora (bounding box) de
um elemento SVG é.

O que é uma caixa delimitadora (bounding box)?

Nem todos os elementos SVG são criados iguais. A coisa poderosa sobre SVG é que suas formas básicas
nos permitem criar todos os tipos de formas não-retangulares: de caminhos arbitrários, para abrir ou
fechar polilinhas e polígonos, com círculos e elipses.

Devido à natureza desses elementos e a sua falta de um modelo de caixa de CSS, a especificação SVG
compensa a falta de um modelo de caixa, introduzindo o conceito de uma caixa delimitadora:

A caixa delimitadora (ou "bbox") de um elemento, é o mais justo retângulo que engloba totalmente o
elemento e seus descendentes, alinhado com os eixos do sistema de coordenadas do usuário desse
elemento.

Em palavras simples, uma caixa delimitadora (bounding box) é o menor retângulo que você pode
desenhar em torno de um elemento, que inclui todos os seus pontos e bordas. Há três tipos de caixas
delimitadoras (bbox):

O "object bounding box" é a caixa delimitadora que contém somente a forma geométrica de um
elemento.

A "stroke bounding box" é a caixa delimitadora que contém a forma geométrica de um elemento e a
forma de seu traçado.

A "decorated bounding box" é a caixa delimitadora que contém a forma geométrica de um elemento, a
forma de seu traçado e de seus marcadores.

Um elemento "bounding box" se caracteriza por propriedades que podem ser recuperadas usando o
método getBBox (), o equivalente SVG de getBoundingClientRect (): x, y, width e height.

var svgElement = document.getElementById('el');


bbox = svgElement.getBBox();
console.log( bbox.x ) ;
console.log( bbox.y ) ;
console.log( bbox.width ) ;
console.log( bbox.height ) ;
Usando a "bounding box" do elemento, podemos simular a presença de um sistema de coordenadas em
torno desse elemento, que podemos usar para posicionar outros elementos.

Mais especificamente, estaremos criando e usando um <svg> interior para estabelecer um novo sistema
de coordenadas em torno de um elemento. As propriedades do <svg> serão definidas pelas propriedades
da caixa delimitadora do elemento: as propriedades x, y, largura (width) e altura (height).

Criando um novo sistema de coordenadas em torno de um elemento SVG

Vamos nos divertir. O pássaro na imagem acima está tentando voltar para o seu ninho. (Minha idéia de
diversão e, é certo, não é divertido).

Normalmente, somos capazes de posicionar o pássaro acima do ninho, especificando sua posição dentro
do SVG usando o sistema de coordenadas da tela SVG inteira.

Podemos certamente fazer isso.

Mas, idealmente, seríamos capazes de posicioná-lo usando valores percentuais que seriam calculados em
relação à "caixa" do ninho. Podemos imitar isso criando um sistema de coordenadas ao redor do ninho
usando o nosso novo elemento <svg>. O elemento <svg> tem seu próprio sistema de coordenadas
estabelecido pela sua largura (width) e altura (height). Usaremos esse sistema de coordenadas para
compensar o sistema de coordenadas ausente no ninho.

Em seguida, movemos o pássaro (o conteúdo do pássaro real) para essa marca <svg>. Por estar contida
pelo <svg>, a posição do pássaro será calculada em relação ao sistema de coordenadas estabelecido no
<svg>.

Mas para criar a conexão relativa entre o pássaro e os elementos do ninho, precisamos do contexto de
posicionamento do pássaro - que é o nosso <svg> - para se assemelhar a um sistema de coordenadas ao
redor do ninho.

Para fazer isso, vamos posicionar o <svg> em cima do ninho, visualmente. É importante notar aqui que
o SVG interno não envolve o ninho - os elementos do ninho não estão contidos dentro da tag <svg>.
Estamos apenas posicionando o <svg> no topo do ninho, visualmente, de modo que parece que a <svg>
é a representação visual do sistema de coordenadas do ninho.

Para determinar a posição exata do <svg> (sua posição x e y na raiz <svg>) e suas dimensões, usaremos as
propriedades da caixa delimitadora do ninho.

A posição do <svg> - os valores x e y - será igual aos valores x e y da caixa delimitadora do ninho. Ou
seja, a caixa delimitadora do grupo de elementos que formam o ninho. (Os grupos podem ter caixas
delimitadoras, assim como elementos simples podem.) O svg interno também terá valores explícitos de
altura e largura que são iguais à altura e largura da caixa delimitadora do ninho.

Aqui está o que parece visualmente:

O que está faltando na imagem acima é o fato que o pássaro não está contido dentro dela. Então é assim
que realmente se parece:
A borda cinza é a borda que representa a caixa delimitadora, e também o novo sistema de coordenadas
ao redor do ninho estabelecido pela svg.

É importante notar aqui que o pássaro está agora posicionado em relação ao sistema de coordenadas do
<svg> interior. Observe como é compensado por uma certa quantidade de pixels de ambos os lados
superior e esquerdo do <svg> interno, assim como ele foi posicionado em relação à raiz <svg>. Isso é
bom para agora. Precisamos nos livrar desse espaço para obter um controle mais fino sobre a posição do
pássaro. Mas vamos chegar a isso em breve.

Outra coisa a observar é que, uma vez que o <svg> interior tem uma altura explícita e largura que são
iguais à altura e largura da caixa delimitadora do ninho, os pés da ave são cortados no fundo devido à
forma como ele está posicionado. Se você tem outros elementos, mais ou menos diferentes em seus
próprios projetos, aqueles podem ser cortados também. Você definitivamente não quer isso. Então, para
contornar isso, você precisa definir explicitamente o valor de estouro para ser visível no <svg> interno.
Isso garantirá que o svg interno se comporte apenas como um contexto de posicionamento, e não como
um contêiner que restringe seu conteúdo a uma área específica visualmente.

O código:

<svg id="birds" xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 3945.8 2400">


<title>Bird & Nest</title>
<g id="nest">
<path ...>
<!-- ... -->
</g>
<!-- O ID que eu estou dando desta SVG é apenas para fins de demonstração -->
<svg x="698" y="1219" width="1055" height="641" viewBox="0 0 1055 641" style="overflow:
visible;" id="coord-sys">
<g id="bird">
<path ...>
<!-- ... -->
</g>
</svg>
</svg>

Assim como com o root <svg>, o valor <viewBox> do SVG interno <svg # coord-sys> é determinado
pelas suas dimensões.

Em seguida, precisamos posicionar o pássaro dentro do novo sistema de coordenadas. Eu não vou me
referir ao svg interno como "svg interno" mais - eu vou me referir a ele como <svg # coord-sys>.

Uma vez que estaremos posicionando o pássaro dentro do svg # coord-sys, precisamos ser capazes de
especificar uma posição para o grupo de elementos que formam este pássaro. Afinal, o pássaro não é
feito de um único elemento - é um grupo de formas. E, portanto, precisamos posicionar um grupo de
elementos, não apenas um elemento. O grupo de elementos que formam o pássaro é envolvido em um
elemento grupo (<g>).

Mas o problema é: o elemento <g> não tem atributos x e y. Portanto, não podemos simplesmente movê-
lo para uma posição específica como:

<g id="bird" x="50%" y="50%">

Normalmente, para mover um grupo de elementos em torno de um SVG, usamos funções de


transformação SVG ou CSS (transformação de "translate", na maioria das vezes). Você pode usar
"transform" para mover o grupo ao redor, certo. Mas isso iria anular toda a idéia que estamos tentando
alcançar e tornaria o novo sistema de coordenadas inútil. Afinal, poderíamos ter usado "transform" para
posicionar o pássaro perto do ninho dentro da raiz <svg> sem ter que criar um novo sistema de
coordenadas.

O que queremos é imitar a forma como elementos são posicionados em CSS, em relação uns aos outros.
Então, para dizer "mover este grupo de elementos para a posição (x, y) dentro deste contexto de
posicionamento particular".

Uma vez que <g> não tem atributos x e y, vamos substituí-lo por outro <svg>.

<svg id="birds" xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 3945.8 2400">


<title>Bird & Nest</title>
<!-- ... -->
<svg x="698" y="1219" width="1055" height="641" viewBox="0 0 1055 641" style="overflow:
visible;">
<svg id="bird">
<!-- ... -->
</svg>
</svg>
</svg>

O svg que envolve o pássaro tem um ID="bird". Este SVG, ao contrário de seu antepassado, servirá
apenas como um recipiente e, mesmo que crie um novo sistema de coordenadas, não usaremos esse
sistema. Usando esta <svg>, agora podemos mover o pássaro em torno do novo sistema de coordenadas
estabelecido em torno (em cima) do ninho.

Neste ponto, é melhor se livrar do espaço branco "offset" ao redor do pássaro. O SVG interno <svg #
bird> tem as mesmas dimensões e viewBox como seu "wrapping" <svg # coord-sys>; O que significa
que, a fim de mover o pássaro ao redor, precisamos ter este espaço em branco em conta. Então, se
quisermos mover o pássaro para posicioná-lo no canto superior esquerdo do sistema, não seremos
capazes de dizer simplesmente x=0 e y=0 - precisaremos usar um deslocamento negativo em ambas as
direções para alcançá-lo . Isso não é prático. Nós também precisamos ter esse deslocamento em conta
onde quer que e como quisermos posicionar o pássaro mais tarde.
Neste ponto, você precisa ser não só familiar, mas também confortável com a forma como o viewBox
funciona. Eu vou assumir que você é.

Vamos alterar o valor da viewBox de <svg # bird> para cortar o espaço em branco para fora. (Então
vamos usar seu sistema de coordenadas, mas só um pouco.)

Por padrão, um svg aninhado ocupará 100% da largura e altura de seu contêiner, a menos que você diga
o contrário.

Então <svg # bird> agora tem as mesmas dimensões exatas que o <svg # coord-sys>. É esse com a beira
cor-de-rosa na seguinte imagem:

Não precisamos que as dimensões sejam diferentes neste exemplo, por isso vamos deixá-las como estão.

A imagem acima também mostra a quantidade de espaço em branco pelo qual o pássaro é deslocado
dentro desse <svg>. Assim, a fim de "unshift", vamos mudar o valor da viewBox do <svg # bird> para
cortar esse espaço em branco fora.

<svg id="birds" xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 3945.8 2400">


<!-- ... -->
<svg x="698" y="1219" width="1055" height="641" style="overflow: visible;">
<svg id="bird" viewBox="150 230 1055 641">
<path ...>
<!-- ... -->
</svg>
</svg>
</svg>

Isso vai mudar o pássaro de modo que seja posicionado no canto superior esquerdo do sistema de
coordenadas. Estou desenfocando <svg # bird> na imagem a seguir, de modo que apenas o sistema de
coordenadas do ninho ainda é mostrado, e a nova posição do pássaro dentro dela:
Então, agora que o pássaro está posicionado no canto superior esquerdo do seu invólucro, podemos
movê-lo ao redor e obter o resultado esperado de cada vez. Por exemplo, se fôssemos mover a ave em
50% em ambas as direções:

<svg id="bird" style="overflow: visible" viewBox="150 230 1055 641" x="50%" y="50%">

Com esta configuração, agora podemos mover o pássaro dentro do sistema de coordenadas do ninho,
assim como moveríamos um elemento HTML dentro de outro em CSS. Ambos os valores de posição
relativa e absoluta trabalham aqui também.

Muito legal, hein? Este é possivelmente o mais próximo que podemos (atualmente) chegar ao
posicionamento relativo no SVG hoje.

Admitimos, para chegar aqui não é o processo mais simples, mas uma vez que você tenha uma boa
compreensão de como trabalha o sistemas de coordenadas SVG e a viewBox, espero que não seja tão
complicado.

Palavras Finais

O exemplo usado neste artigo é um exemplo muito específico e não é, certamente, o caso de uso mais
prático de todos os tempos. Seu (s) caso (s) de uso provavelmente serão completamente diferentes. Você
pode estar trabalhando com um SVG muito diferente, onde você pode não precisar fazer nenhum corte
viewBox. Se você criar seu SVG você mesmo, você pode posicionar seu elemento (por exemplo, o
pássaro em nosso caso) no canto superior esquerdo da tela SVG, de modo que quando você envolve-lo
em outro svg, ele também seria posicionado no canto superior esquerdo e Você não teria que fazer
nenhum corte em tudo. Deixei este exemplo um pouco mais complexo apenas para que possamos cobrir
mais cenários. (E porque eu era um pouco preguiçoso para editar o SVG no Illustrator depois de ter
escrito metade deste artigo.Mas eu continuo querendo negar isso).

Mas o takeaway é como imitar o posicionamento relativo usando svgs aninhados. Se você usa um nível,
dois níveis ou mais, os conceitos são os mesmos.
Você pode achar esta técnica útil para posicionar elementos SVG UI em relação uns aos outros. Ou
talvez posicionamento relativo em SVGs criados dinamicamente. Sua imaginação é o limite.

Espero que você tenha encontrado este artigo útil. Obrigado por ler!

Você também pode gostar