¿Cómo usar el índice z en elementos svg?

154

Estoy usando los círculos svg en mi proyecto así,

<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>

Y estoy usando el índice z en la getiqueta para mostrar los elementos primero. En mi proyecto, necesito usar solo el valor del índice z, pero no puedo usar el índice z para mis elementos svg. He buscado mucho en Google pero no encontré nada relativamente. Entonces, por favor, ayúdame a usar el índice z en mi svg.

Aquí está la DEMO.

Karthi Keyan
fuente

Respuestas:

163

Especificación

En la especificación SVG versión 1.1, el orden de representación se basa en el orden del documento:

first element -> "painted" first

Referencia a la SVG 1.1. Especificación

3.3 Orden de representación

Los elementos en un fragmento de documento SVG tienen un orden de dibujo implícito, y los primeros elementos en el fragmento de documento SVG se "pintan" primero . Los elementos posteriores se pintan encima de los elementos pintados previamente.


Solución (más limpia-más rápida)

Debe colocar el círculo verde como el último objeto que se dibujará. Entonces intercambie los dos 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>

Aquí el tenedor de tu jsFiddle .

Solución (alternativa)

La etiqueta usecon el atributo xlink:hrefy como valor el id del elemento. Tenga en cuenta que podría no ser la mejor solución, incluso si el resultado parece estar bien. Teniendo un poco de tiempo, aquí el enlace de la especificación SVG 1.1 "use" Element .

Propósito:

Para evitar que los autores modifiquen el documento al que se hace referencia para agregar una ID al elemento raíz.

<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

La especificación SVG 2 es la próxima versión principal y aún admite las características anteriores.

3.4. Orden de representación

Los elementos en SVG se posicionan en tres dimensiones. Además de su posición en los ejes xey de la ventana gráfica SVG, los elementos SVG también se colocan en el eje z. La posición en el eje z define el orden en que se pintan .

A lo largo del eje z, los elementos se agrupan en contextos de apilamiento.

3.4.1. Establecer un contexto de apilamiento en SVG

...

Los contextos de apilamiento son herramientas conceptuales utilizadas para describir el orden en que los elementos deben pintarse uno encima del otro cuando se procesa el documento, ...

Maicolpt
fuente
También hay un borrador antiguo sobre la anulación del orden de representación, pero es una función que no está disponible. Borrador de referencia
Maicolpt
12
¡Ay! No siempre es fácil dibujar los elementos en el orden en que desea pintarlos, especialmente si los objetos se están generando mediante programación y pueden aparecer anidados (por ejemplo, parece que g no puede contener a, b, de modo que a esté debajo de g hermano c pero b está por encima)
Michael
@ Michael: En su escenario, primero intentaré comprender si realmente los elementos tienen que agruparse.
Maicolpt
1
¡Ese 'uso de xlink: href' es genial, extraño y perfecto para lo que necesito!
Ian
32

Como otros han dicho aquí, el índice z se define por el orden en que el elemento aparece en el DOM. Si reordenar manualmente su html no es una opción o sería difícil, puede usar D3 para reordenar grupos / objetos SVG.

Use D3 para actualizar el orden DOM y la funcionalidad del índice Z de Mimic

Actualización del índice Z del elemento SVG con D3

En el nivel más básico (y si no está usando ID para otra cosa), puede usar ID de elementos como sustituto para el índice z y reordenar con ellos. Más allá de eso, puedes dejar volar tu imaginación.

Ejemplos en fragmento 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>

La idea básica es:

  1. Use D3 para seleccionar los elementos DOM SVG.

    var circles = d3.selectAll('circle')
  2. Cree una serie de índices z con una relación 1: 1 con sus elementos SVG (que desea reordenar). Las matrices de índice Z utilizadas en los ejemplos a continuación son ID, posición x e y, radios, 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. Luego, use D3 para vincular sus índices z a esa selección.

    circles.data(zOrders[setOrderBy]);
  4. Por último, llame a D3.sort para reordenar los elementos en el DOM en función de los datos.

    circles.sort(setOrder);

Ejemplos

ingrese la descripción de la imagen aquí

  • Puedes apilar por ID

ingrese la descripción de la imagen aquí

  • Con SVG más a la izquierda en la parte superior

ingrese la descripción de la imagen aquí

  • Radios más pequeños en la parte superior

ingrese la descripción de la imagen aquí

  • O especifique una matriz para aplicar el índice z para un orden específico: en mi código de ejemplo, la matriz [3,4,1,2,5]mueve / reordena el tercer círculo (en el orden HTML original) para ser el primero en el DOM, el cuarto para ser el segundo, el primero para ser el tercero , y así...

Steve Ladavich
fuente
Definitivamente la mejor respuesta aquí ... 10/10. ¿Por qué se acepta esto ahora?
Tigerrrrr
1
@Tigerrrrrr Importar una biblioteca externa para hacer algo tan simple como controlar el orden de apilamiento es una locura. Para empeorar las cosas, D3 es una biblioteca particularmente grande.
iMe
2
@Me, bien dicho. Si bien esta es una solución a un problema, lo hace digno de estar aquí; no es, y nunca debería ser, la respuesta. Lo único que debería reemplazar la respuesta actual sería si sale una nueva especificación y cambian los navegadores. Cualquiera que quiera usar esta respuesta, no importe todos los D3, solo importe los módulos que necesita. NO IMPORTE TODO D3 solo por esto.
Steve Ladavich
31

Intenta invertir #oney #two. Echa un vistazo a este violín: http://jsfiddle.net/hu2pk/3/

Update

En SVG, el índice z se define por el orden en que aparece el elemento en el documento . También puede echar un vistazo a esta página si lo desea: https://stackoverflow.com/a/482147/1932751

Lucas Willems
fuente
1
Gracias pero necesito el elemento basado en el valor del índice z.
Karthi Keyan
Okay. ¿Y quieres #one está en #two o lo contrario?
Lucas Willems
si dije valor del índice z como -1 para #one significa que se mostrará en el nivel superior.
Karthi Keyan
10
No hay ninguna propiedad de índice z en ninguna de las especificaciones SVG. La única forma de definir qué elementos aparecen en la parte superior y cuáles aparecen en la parte inferior es mediante el uso de pedidos DOM
nicholaswmin
9
d3.selection.prototype.moveToFront = function() { return this.each(function() { this.parentNode.appendChild(this); }); };Y luego puede decir a selection.moveToFront()través de stackoverflow.com/questions/14167863/…
mb21
21

Puedes usar el uso .

<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>

El círculo verde aparece en la parte superior.
jsFiddle

Jose Rui Santos
fuente
44
¿Esto dibuja #one dos veces?
mareoraft
@mareoraft Sí, #onese dibuja dos veces. Pero si lo desea, puede ocultar la primera instancia a través de CSS. usetiene el mismo efecto que clonar el elemento DOM referido
Jose Rui Santos
+1 porque no necesita JavaScript pero -1 porque también podría cambiar el orden de <g>sí mismo al cambiar el DOM antes de cargar de todos modos.
Hafenkranich
14

Como se discutió, los svgs se procesan en orden y no tienen en cuenta el índice z (por ahora). Tal vez solo envíe el elemento específico a la parte inferior de su padre para que se procese al final.

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"));

Trabajó para mi.

Actualizar

Si tiene un SVG anidado, que contiene grupos, deberá sacar el elemento de su nodo principal.

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

Una buena característica de SVG es que cada elemento contiene su ubicación independientemente del grupo en el que esté anidado: +1:

bumsoverboard
fuente
hola, esto funcionó para mí, pero ¿cuál sería el equivalente 'traer al fondo'? Gracias
gavin
Los elementos SVG @gavin se dibujan en orden de arriba hacia abajo. Para poner un elemento en la parte superior, lo agregamos () para que sea el último elemento. Por el contrario, si queremos que un elemento se envíe al fondo, lo colocamos como el primer elemento mediante prepend (). función bringToBottomofSVG (targetElement) {let parent = targetElement.ownerSVGElement; parent.prepend (targetElement); }
bumsoverboard
13

Usando D3:

Si desea volver a insertar cada elemento seleccionado, en orden, como el último hijo de su padre.

selection.raise()
Diso
fuente
55
selection.raise()es nuevo en D3 a partir de v4.
tephyr el
9

No hay índice z para svgs. Pero svg determina cuáles de sus elementos son los más altos por su posición en el DOM. Por lo tanto, puede eliminar el Objeto y colocarlo al final del svg, convirtiéndolo en el elemento "último renderizado". Ese se convierte en "superior" 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
fuente
es 2019 ahora, ¿sigue siendo cierto? ¿Como SVG 2.0 ganó adopción en los navegadores modernos?
Andrew S
4

Las soluciones limpias, rápidas y fáciles publicadas a la fecha de esta respuesta no son satisfactorias. Se construyen sobre la afirmación errónea de que los documentos SVG carecen de orden z. Las bibliotecas tampoco son necesarias. Una línea de código puede realizar la mayoría de las operaciones para manipular el orden z de objetos o grupos de objetos que podrían ser necesarios en el desarrollo de una aplicación que mueva objetos 2D en un espacio xyz.

El orden Z definitivamente existe en los fragmentos de documentos SVG

Lo que se llama un fragmento de documento SVG es un árbol de elementos derivados del tipo de nodo base SVGElement. El nodo raíz de un fragmento de documento SVG es un elemento SVGSVGE, que corresponde a una etiqueta HTML5 <svg> . SVGGElement corresponde a la etiqueta <g> y permite agregar elementos secundarios .

Tener un atributo de índice z en el elemento SVGE como en CSS derrotaría al modelo de representación SVG. Las secciones 3.3 y 3.4 de la Recomendación v3.1 SV3 del W3C, segunda edición, establecen que los fragmentos de documentos SVG (árboles de descendientes de un elemento SVGSVGE) se procesan utilizando lo que se denomina una primera búsqueda en profundidad del árbol . Ese esquema es un orden az en todos los sentidos del término.

El orden Z es en realidad un atajo de visión por computadora para evitar la necesidad de una verdadera representación 3D con las complejidades y las demandas informáticas del trazado de rayos. La ecuación lineal para el índice z implícito de elementos en un fragmento de documento SVG.

z-index = z-index_of_svg_tag + depth_first_tree_index / tree_node_qty

Esto es importante porque si desea mover un círculo que estaba debajo de un cuadrado hacia arriba, simplemente inserte el cuadrado antes del círculo. Esto se puede hacer fácilmente en JavaScript.

Métodos de apoyo

Las instancias de SVGElement tienen dos métodos que admiten la manipulación simple y fácil del orden z.

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

La respuesta correcta que no crea un desastre

Debido a que SVGGElement ( etiqueta <g> ) se puede quitar e insertar tan fácilmente como un SVGCircleElement o cualquier otra forma, las capas de imagen típicas de los productos de Adobe y otras herramientas gráficas se pueden implementar con facilidad usando SVGGElement. Este JavaScript es esencialmente un comando Mover a continuación .

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

Si la capa de un robot dibujado como hijos de SVGGElement gRobot estaba antes de la puerta dibujada como hijos de SVGGElement gDoorway, el robot ahora está detrás de la puerta porque el orden z de la puerta ahora es uno más el orden z del robot.

Un comando Mover arriba es casi tan fácil.

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

Simplemente piense a = a y b = b para recordar esto.

insert after = move above
insert before = move below

Dejar el DOM en un estado coherente con la vista

La razón por la cual esta respuesta es correcta es porque es mínima y completa y, al igual que las partes internas de los productos de Adobe u otros editores de gráficos bien diseñados, deja la representación interna en un estado que es consistente con la vista creada al renderizar.

Enfoque alternativo pero limitado

Otro enfoque comúnmente utilizado es usar CSS z-index junto con múltiples fragmentos de documentos SVG (etiquetas SVG) con fondos mayormente transparentes en todos, excepto en el inferior. Nuevamente, esto derrota la elegancia del modelo de renderizado SVG, lo que dificulta mover los objetos hacia arriba o hacia abajo en el orden z.


NOTAS

  1. ( https://www.w3.org/TR/SVG/render.html v 1.1, segunda edición, 16 de agosto de 2011)

    3.3 Elementos de orden de representación en un fragmento de documento SVG tienen un orden de dibujo implícito, con los primeros elementos en el fragmento de documento SVG "pintados" primero. Los elementos posteriores se pintan encima de los elementos pintados previamente.

    3.4 Cómo se representan los grupos Los elementos de agrupación como el elemento 'g' (ver elementos contenedores) tienen el efecto de producir un lienzo separado temporal inicializado en negro transparente sobre el que se pintan los elementos secundarios. Al finalizar el grupo, los efectos de filtro especificados para el grupo se aplican para crear un lienzo temporal modificado. El lienzo temporal modificado se compone en segundo plano, teniendo en cuenta cualquier configuración de enmascaramiento y opacidad a nivel de grupo en el grupo.

Douglas Daseeco
fuente
4

Ya tenemos 2019 y z-indextodavía no es compatible con SVG.

Puede ver en el sitio soporte SVG2 en Mozilla que el estado para z-index- No implementado .

También puede ver en el sitio el error 360148 "Soporte de la propiedad 'z-index' en elementos SVG" (Reportado: hace 12 años).

Pero tienes 3 posibilidades en SVG para configurarlo:

  1. Con element.appendChild(aChild);
  2. Con parentNode.insertBefore(newNode, referenceNode);
  3. Con targetElement.insertAdjacentElement(positionStr, newElement);(Sin soporte en IE para SVG)

Ejemplo de demostración interactiva

Con todo esto 3 funciones.

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
fuente
2

Presione el elemento SVG para que dure, de modo que su índice z esté en la parte superior. En SVG, no hay ninguna propiedad llamada índice z. intente debajo de javascript para llevar el elemento al principio.

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

Objetivo: es un elemento para el que debemos llevarlo al principio svg: es el contenedor de elementos

Vaseem AbdulSamad
fuente
0

es fácil hacerlo:

  1. clona tus artículos
  2. ordenar elementos clonados
  3. reemplazar elementos 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
fuente