Como usar o z-index nos elementos svg?

154

Estou usando os círculos svg no meu projeto assim,

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 120">
    <g>
        <g id="one">
            <circle fill="green" cx="100" cy="105" r="20" />
        </g>
        <g id="two">
            <circle fill="orange" cx="100" cy="95" r="20" />
        </g>
    </g>
</svg>

E eu estou usando o z-index na gtag para mostrar os elementos primeiro. No meu projeto, preciso usar apenas o valor do índice z, mas não posso usar o índice z nos meus elementos svg. Pesquisei bastante no Google, mas não encontrei nada relativamente. Então, por favor me ajude a usar o z-index no meu svg.

Aqui está a demo.

Karthi Keyan
fonte

Respostas:

163

Especificação

Na especificação SVG versão 1.1, a ordem de renderização é baseada na ordem do documento:

first element -> "painted" first

Referência ao SVG 1.1. Especificação

3.3 Ordem de renderização

Os elementos em um fragmento de documento SVG têm uma ordem implícita de desenho, com os primeiros elementos no fragmento de documento SVG sendo "pintados" primeiro . Os elementos subsequentes são pintados em cima dos elementos pintados anteriormente.


Solução (limpador mais rápido)

Você deve colocar o círculo verde como o último objeto a ser desenhado. Então troque os dois elementos.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="30 70 160 120"> 
   <!-- First draw the orange circle -->
   <circle fill="orange" cx="100" cy="95" r="20"/> 

   <!-- Then draw the green circle over the current canvas -->
   <circle fill="green" cx="100" cy="105" r="20"/> 
</svg>

Aqui o garfo do seu jsFiddle .

Solução (alternativa)

A tag usecom o atributo xlink:hrefe, como valor, o ID do elemento. Lembre-se de que talvez não seja a melhor solução, mesmo que o resultado pareça bom. Tendo um pouco de tempo, aqui o link da especificação SVG 1.1 "use" Element .

Objetivo:

Para evitar que os autores modifiquem o documento referenciado para adicionar um ID ao elemento raiz.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="30 70 160 120">
    <!-- First draw the green circle -->
    <circle id="one" fill="green" cx="100" cy="105" r="20" />
    
    <!-- Then draw the orange circle over the current canvas -->
    <circle id="two" fill="orange" cx="100" cy="95" r="20" />
    
    <!-- Finally draw again the green circle over the current canvas -->
    <use xlink:href="#one"/>
</svg>


Notas sobre SVG 2

A especificação SVG 2 é a próxima versão principal e ainda suporta os recursos acima.

3.4 Ordem de renderização

Os elementos no SVG são posicionados em três dimensões. Além de sua posição nos eixos xey da viewport SVG, os elementos SVG também são posicionados no eixo z. A posição no eixo z define a ordem em que eles são pintados .

Ao longo do eixo z, os elementos são agrupados em contextos de empilhamento.

3.4.1 Estabelecendo um contexto de empilhamento no SVG

...

Os contextos de empilhamento são ferramentas conceituais usadas para descrever a ordem em que os elementos devem ser pintados um por cima do outro quando o documento é renderizado, ...

Maicolpt
fonte
Há também um rascunho antigo sobre a substituição da ordem de renderização, mas esse recurso não está disponível. Draft Reference
Maicolpt 14/08/14
12
caramba! nem sempre é fácil desenhar os elementos na ordem em que você deseja pintá-los, especialmente se os objetos estão sendo gerados programaticamente e podem parecer aninhados (por exemplo, parece que g não pode conter a, b, de modo que a esteja abaixo de g irmão c b mas é acima dela)
Michael
@ Michael: No seu cenário, primeiro tentarei entender se realmente os elementos precisam ser agrupados.
Maicolpt
1
Esse 'use xlink: href' é legal, esquisito e perfeito para o que eu preciso !!
25418 Ian
32

Como outros aqui disseram, o índice z é definido pela ordem em que o elemento aparece no DOM. Se reordenar manualmente o html não for uma opção ou for difícil, use o D3 para reordenar grupos / objetos SVG.

Use D3 para atualizar a ordem do DOM e imitar a funcionalidade do índice Z

Atualizando o índice Z do elemento SVG com D3

No nível mais básico (e se você não estiver usando IDs para mais nada), é possível usar IDs de elemento como substituto do índice z e reordená-los. Além disso, você pode praticamente deixar sua imaginação correr solta.

Exemplos no snippet de código

var circles = d3.selectAll('circle')
var label = d3.select('svg').append('text')
    .attr('transform', 'translate(' + [5,100] + ')')

var zOrders = {
    IDs: circles[0].map(function(cv){ return cv.id; }),
    xPos: circles[0].map(function(cv){ return cv.cx.baseVal.value; }),
    yPos: circles[0].map(function(cv){ return cv.cy.baseVal.value; }),
    radii: circles[0].map(function(cv){ return cv.r.baseVal.value; }),
    customOrder: [3, 4, 1, 2, 5]
}

var setOrderBy = 'IDs';
var setOrder = d3.descending;

label.text(setOrderBy);
circles.data(zOrders[setOrderBy])
circles.sort(setOrder);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 100"> 
  <circle id="1" fill="green" cx="50" cy="40" r="20"/> 
  <circle id="2" fill="orange" cx="60" cy="50" r="18"/>
  <circle id="3" fill="red" cx="40" cy="55" r="10"/> 
  <circle id="4" fill="blue" cx="70" cy="20" r="30"/> 
  <circle id="5" fill="pink" cx="35" cy="20" r="15"/> 
</svg>

A ideia básica é:

  1. Use D3 para selecionar os elementos SVG DOM.

    var circles = d3.selectAll('circle')
  2. Crie uma matriz de índices z com um relacionamento 1: 1 com seus elementos SVG (que você deseja reordenar). Matrizes de índice Z usadas nos exemplos abaixo são IDs, posição x e y, raios, etc.

    var zOrders = {
        IDs: circles[0].map(function(cv){ return cv.id; }),
        xPos: circles[0].map(function(cv){ return cv.cx.baseVal.value; }),
        yPos: circles[0].map(function(cv){ return cv.cy.baseVal.value; }),
        radii: circles[0].map(function(cv){ return cv.r.baseVal.value; }),
        customOrder: [3, 4, 1, 2, 5]
    }
  3. Em seguida, use D3 para vincular seus índices z a essa seleção.

    circles.data(zOrders[setOrderBy]);
  4. Por fim, chame D3.sort para reordenar os elementos no DOM com base nos dados.

    circles.sort(setOrder);

Exemplos

insira a descrição da imagem aqui

  • Você pode empilhar por ID

insira a descrição da imagem aqui

  • Com SVG mais à esquerda no topo

insira a descrição da imagem aqui

  • Raios menores no topo

insira a descrição da imagem aqui

  • Ou Especifique uma matriz para aplicar o z-index a uma ordem específica - no meu exemplo de código, a matriz [3,4,1,2,5]move / reordena o terceiro círculo (na ordem HTML original) para ser o 1º no DOM, o 4º para o 2º, o 1º para o 3º , e assim por diante...

Steve Ladavich
fonte
Definitivamente a melhor resposta aqui ... 10/10. Por que isso agora é aceito?
Tigerrrrr
1
@Tigerrrrrr Importar uma biblioteca externa para fazer algo tão simples quanto controlar a ordem de empilhamento é loucura. Para piorar ainda mais, o D3 é uma biblioteca particularmente grande.
iMe 23/02
2
@ iMe, bem dito. Embora essa seja uma solução para um problema, vale a pena estar aqui; não é, e nunca deve ser, a resposta. A única coisa que deveria substituir a resposta atual seria se uma especificação mais recente surgisse e os navegadores mudassem. Quem quiser usar esta resposta, não importe todo o D3, apenas importe os módulos necessários. NÃO IMPORTE TODO O D3 apenas para isso.
Steve Ladavich
31

Tente inverter #onee #two. Dê uma olhada neste violino: http://jsfiddle.net/hu2pk/3/

Update

No SVG, o índice z é definido pela ordem em que o elemento aparece no documento . Você também pode consultar esta página se quiser: https://stackoverflow.com/a/482147/1932751

Lucas Willems
fonte
1
Obrigado, mas eu preciso do elemento com base no valor de z-index.
Karthi Keyan
Está bem. E você quer #um está no #two ou o oposto?
Lucas Willems
ya, se eu disser o valor do índice z como -1 para #one significa que ele será exibido no nível superior.
Karthi Keyan
10
Não há propriedade de índice z em nenhuma das especificações SVG. A única maneira de definir quais elementos aparecem no topo e que aparecem ao fundo é usando DOM ordenação
nicholaswmin
9
d3.selection.prototype.moveToFront = function() { return this.each(function() { this.parentNode.appendChild(this); }); };E então você pode dizer selection.moveToFront()via stackoverflow.com/questions/14167863/...
MB21
21

Você pode usar use .

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 120">
    <g>
        <g id="one">
            <circle fill="green" cx="100" cy="105" r="20" />
        </g>
        <g id="two">
            <circle fill="orange" cx="100" cy="95" r="20" />
        </g>
    </g>
    <use xlink:href="#one" />
</svg>

O círculo verde aparece no topo.
jsFiddle

Jose Rui Santos
fonte
4
Isso atrai # um duas vezes?
Mareoraft
@mareoraft Sim, #oneé sorteado duas vezes. Mas se você quiser, pode ocultar a primeira instância por meio do CSS. usetem o mesmo efeito que a clonagem do referido elemento DOM
José Rui Santos
+1 porque não precisa de javascript, mas -1 porque você também pode alterar a ordem em <g>si ao alterar o DOM antes de carregar de qualquer maneira.
Hafenkranich 25/10
14

Conforme discutido, os svgs são renderizados em ordem e não levam em consideração o z-index (por enquanto). Talvez apenas envie o elemento específico para a parte inferior do pai, para que seja renderizado por último.

function bringToTop(targetElement){
  // put the element at the bottom of its parent
  let parent = targetElement.parentNode;
  parent.appendChild(targetElement);
}

// then just pass through the element you wish to bring to the top
bringToTop(document.getElementById("one"));

Trabalhou para mim.

Atualizar

Se você tiver um SVG aninhado, contendo grupos, será necessário trazer o item para fora de seu parentNode.

function bringToTopofSVG(targetElement){
  let parent = targetElement.ownerSVGElement;
  parent.appendChild(targetElement);
}

Um bom recurso dos SVGs é que cada elemento contém sua localização, independentemente do grupo em que está aninhado: +1:

prancha de surf
fonte
oi, isso funcionou para mim, mas qual seria o equivalente 'trazer para o fundo'? Obrigado
gavin
Os elementos SVG do @gavin são desenhados em ordem de cima para baixo. Para colocar um elemento no topo, anexamos () para que ele seja o último elemento. Por outro lado, se queremos que um elemento seja enviado para o fundo, o colocamos como o primeiro elemento por prepend (). função bringToBottomofSVG (targetElement) {deixe pai = targetElement.ownerSVGElement; parent.prepend (targetElement); }
bumsoverboard 07/07/19
13

Usando D3:

Se você deseja reinserir cada elemento selecionado, em ordem, como o último filho de seu pai.

selection.raise()
Diso
fonte
5
selection.raise()é novo no D3 a partir da v4.
Tephyr
9

Não há z-index para svgs. Mas svg determina quais dos seus elementos são os mais altos por sua posição no DOM. Assim, você pode remover o Objeto e colocá-lo no final do svg, tornando-o o último elemento renderizado. Aquele é então renderizado "no topo" visualmente.


Usando jQuery:

function moveUp(thisObject){
    thisObject.appendTo(thisObject.parents('svg>g'));
}

uso:

moveUp($('#myTopElement'));

Usando D3.js:

d3.selection.prototype.moveUp = function() {
    return this.each(function() {
        this.parentNode.appendChild(this);
    });
};

uso:

myTopElement.moveUp();

Hafenkranich
fonte
seu 2019 agora, isso ainda é verdade? Como o SVG 2.0 ganhou adoção nos navegadores modernos?
Andrew S
4

As soluções limpas, rápidas e fáceis publicadas na data desta resposta são insatisfatórias. Eles são construídos sobre a afirmação falsa de que os documentos SVG não possuem ordem z. Bibliotecas também não são necessárias. Uma linha de código pode executar a maioria das operações para manipular a ordem z de objetos ou grupos de objetos que podem ser necessários no desenvolvimento de um aplicativo que move objetos 2D em um espaço xyz.

A ordem Z existe definitivamente em fragmentos de documento SVG

O que é chamado de fragmento de documento SVG é uma árvore de elementos derivados do tipo de nó base SVGElement. O nó raiz de um fragmento de documento SVG é um SVGSVGElement, que corresponde a uma tag HTML5 <svg> . O SVGGElement corresponde à tag <g> e permite agregar filhos.

Ter um atributo z-index no SVGElement como no CSS derrotaria o modelo de renderização SVG. As seções 3.3 e 3.4 da Recomendação W3C SVG v1.1 2ª Edição afirmam que fragmentos de documento SVG (árvores de descendentes de um SVGSVGElement) são renderizados usando o que é chamado de pesquisa inicial em profundidade da árvore . Esse esquema é uma ordem em todos os sentidos do termo.

A ordem Z é na verdade um atalho de visão computacional para evitar a necessidade de renderização 3D verdadeira com as complexidades e demandas computacionais do traçado de raios. A equação linear para o índice z implícito de elementos em um fragmento de documento SVG.

z-index = z-index_of_svg_tag + depth_first_tree_index / tree_node_qty

Isso é importante porque se você deseja mover um círculo que estava abaixo de um quadrado para acima dele, basta inserir o quadrado antes do círculo. Isso pode ser feito facilmente em JavaScript.

Métodos de suporte

As instâncias do SVGElement têm dois métodos que oferecem suporte à manipulação simples e fácil da ordem z.

  • parent.removeChild (child)
  • parent.insertBefore (child, childRef)

A resposta correta que não cria uma bagunça

Como a SVGGElement ( tag <g> ) pode ser removida e inserida tão facilmente quanto um SVGCircleElement ou qualquer outra forma, as camadas de imagem típicas dos produtos Adobe e outras ferramentas gráficas podem ser implementadas com facilidade usando o SVGGElement. Esse JavaScript é essencialmente um comando Mover para baixo .

parent.insertBefore(parent.removeChild(gRobot), gDoorway)

Se a camada de um robô desenhada como filho de SVGGElement gRobot estava antes da porta desenhada como filho de SVGGElement gDoorway, o robô agora está atrás da porta porque a ordem z da porta agora é uma mais a ordem z do robô.

Um comando Mover para cima é quase tão fácil.

parent.insertBefore(parent.removeChild(gRobot), gDoorway.nextSibling())

Basta pensar em a = aeb = b para lembrar disso.

insert after = move above
insert before = move below

Deixando o DOM em um estado consistente com a exibição

A razão pela qual essa resposta está correta é porque é mínima e completa e, como os produtos internos da Adobe ou outros editores de gráficos bem projetados, deixa a representação interna em um estado consistente com a exibição criada pela renderização.

Abordagem alternativa, mas limitada

Outra abordagem comumente usada é usar o índice z CSS em conjunto com vários fragmentos de documento SVG (tags SVG), com fundos transparentes na maioria dos casos, exceto o inferior. Novamente, isso anula a elegância do modelo de renderização SVG, dificultando a movimentação de objetos para cima ou para baixo na ordem z.


NOTAS:

  1. ( https://www.w3.org/TR/SVG/render.html v 1.1, 2ª edição, 16 de agosto de 2011)

    3.3 Os elementos da ordem de renderização em um fragmento de documento SVG têm uma ordem implícita de desenho, com os primeiros elementos no fragmento de documento SVG sendo "pintados" primeiro. Os elementos subsequentes são pintados sobre os elementos pintados anteriormente.

    3.4 Como os grupos são renderizados Elementos de agrupamento, como o elemento 'g' (consulte elementos de contêiner), produzem uma tela separada temporária, inicializada em preto transparente, na qual os elementos filhos são pintados. Após a conclusão do grupo, quaisquer efeitos de filtro especificados para o grupo são aplicados para criar uma tela temporária modificada. A tela temporária modificada é composta em segundo plano, levando em consideração as configurações de máscara e opacidade no nível do grupo.

Douglas Daseeco
fonte
4

Já temos 2019 e z-indexainda não temos suporte no SVG.

Você pode ver no site SVG2 support no Mozilla que o estado para z-index- Não implementado .

Você também pode ver no site Bug 360148 "Suporte à propriedade 'z-index' nos elementos SVG" (Reportado: 12 anos atrás).

Mas você tem 3 possibilidades no SVG para configurá-lo:

  1. Com element.appendChild(aChild);
  2. Com parentNode.insertBefore(newNode, referenceNode);
  3. With targetElement.insertAdjacentElement(positionStr, newElement);(sem suporte no IE para SVG)

Exemplo de demonstração interativa

Com todas essas 3 funções.

var state = 0,
    index = 100;

document.onclick = function(e)
{
    if(e.target.getAttribute('class') == 'clickable')
    {
        var parent = e.target.parentNode;

        if(state == 0)
            parent.appendChild(e.target);
        else if(state == 1)
            parent.insertBefore(e.target, null); //null - adds it on the end
        else if(state == 2)
            parent.insertAdjacentElement('beforeend', e.target);
        else
            e.target.style.zIndex = index++;
    }
};

if(!document.querySelector('svg').insertAdjacentElement)
{
    var label = document.querySelectorAll('label')[2];
    label.setAttribute('disabled','disabled');
    label.style.color = '#aaa';
    label.style.background = '#eee';
    label.style.cursor = 'not-allowed';
    label.title = 'This function is not supported in SVG for your browser.';
}
label{background:#cef;padding:5px;cursor:pointer}
.clickable{cursor:pointer}
With: 
<label><input type="radio" name="check" onclick="state=0" checked/>appendChild()</label>
<label><input type="radio" name="check" onclick="state=1"/>insertBefore()</label><br><br>
<label><input type="radio" name="check" onclick="state=2"/>insertAdjacentElement()</label>
<label><input type="radio" name="check" onclick="state=3"/>Try it with z-index</label>
<br>
<svg width="150" height="150" viewBox="0 0 150 150">
    <g stroke="none">
        <rect id="i1" class="clickable" x="10" y="10" width="50" height="50" fill="#80f"/>
        <rect id="i2" class="clickable" x="40" y="40" width="50" height="50" fill="#8f0"/>
        <rect id="i3" class="clickable" x="70" y="70" width="50" height="50" fill="#08f"/>
    </g>
</svg>

Bharata
fonte
2

Pressione o elemento SVG para durar, para que seu índice z fique no topo. No SVG, não há nenhuma propriedade chamada z-index. tente abaixo do javascript para trazer o elemento para o topo.

var Target = document.getElementById(event.currentTarget.id);
var svg = document.getElementById("SVGEditor");
svg.insertBefore(Target, svg.lastChild.nextSibling);

Alvo: É um elemento para o qual precisamos trazê-lo para o topo svg: É o contêiner de elementos

Vaseem AbdulSamad
fonte
0

é fácil fazê-lo:

  1. clonar seus itens
  2. classificar itens clonados
  3. substituir itens por clonados

function rebuildElementsOrder( selector, orderAttr, sortFnCallback ) {
	let $items = $(selector);
	let $cloned = $items.clone();
	
	$cloned.sort(sortFnCallback != null ? sortFnCallback : function(a,b) {
  		let i0 = a.getAttribute(orderAttr)?parseInt(a.getAttribute(orderAttr)):0,
  		    i1 = b.getAttribute(orderAttr)?parseInt(b.getAttribute(orderAttr)):0;
  		return i0 > i1?1:-1;
	});

        $items.each(function(i, e){
            e.replaceWith($cloned[i]);
	})
}

$('use[order]').click(function() {
    rebuildElementsOrder('use[order]', 'order');

    /* you can use z-index property for inline css declaration
    ** getComputedStyle always return "auto" in both Internal and External CSS decl [tested in chrome]
    
    rebuildElementsOrder( 'use[order]', null, function(a, b) {
        let i0 = a.style.zIndex?parseInt(a.style.zIndex):0,
  		    i1 = b.style.zIndex?parseInt(b.style.zIndex):0;
  		return i0 > i1?1:-1;
    });
    */
});
use[order] {
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="keybContainer" viewBox="0 0 150 150" xml:space="preserve">
<defs>
    <symbol id="sym-cr" preserveAspectRatio="xMidYMid meet" viewBox="0 0 60 60">
        <circle cx="30" cy="30" r="30" />
        <text x="30" y="30" text-anchor="middle" font-size="0.45em" fill="white">
            <tspan dy="0.2em">Click to reorder</tspan>
        </text>
    </symbol>
</defs>
    <use order="1" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sym-cr" x="0" y="0" width="60" height="60" style="fill: #ff9700; z-index: 1;"></use>
    <use order="4" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sym-cr" x="50" y="20" width="50" height="50" style="fill: #0D47A1; z-index: 4;"></use>
    <use order="5" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sym-cr" x="15" y="30" width="50" height="40" style="fill: #9E9E9E; z-index: 5;"></use>
    <use order="3" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sym-cr" x="25" y="30" width="80" height="80" style="fill: #D1E163; z-index: 3;"></use>
    <use order="2" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sym-cr" x="30" y="0" width="50" height="70" style="fill: #00BCD4; z-index: 2;"></use>
    <use order="0" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sym-cr" x="5" y="5" width="100" height="100" style="fill: #E91E63; z-index: 0;"></use>
</svg>

tdjprog
fonte