Comment utiliser z-index dans les éléments svg?

154

J'utilise les cercles svg dans mon projet comme ça,

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

Et j'utilise le z-index dans la gbalise pour montrer les éléments en premier. Dans mon projet, je dois utiliser uniquement la valeur z-index, mais je ne peux pas utiliser le z-index pour mes éléments svg. J'ai beaucoup cherché sur Google mais je n'ai rien trouvé de relativement. Alors s'il vous plaît, aidez-moi à utiliser z-index dans mon svg.

Voici la DEMO.

Karthi Keyan
la source

Réponses:

163

spécification

Dans la version 1.1 de la spécification SVG, l'ordre de rendu est basé sur l'ordre des documents:

first element -> "painted" first

Référence au SVG 1.1. spécification

3.3 Ordre de rendu

Les éléments d'un fragment de document SVG ont un ordre de dessin implicite, les premiers éléments du fragment de document SVG étant "peints" en premier . Les éléments suivants sont peints au-dessus des éléments déjà peints.


Solution (plus propre plus rapide)

Vous devez mettre le cercle vert comme dernier objet à dessiner. Alors échangez les deux éléments.

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

Voici le fork de votre jsFiddle .

Solution (alternative)

La balise useavec l'attribut xlink:hrefet comme valeur l'id de l'élément. Gardez à l'esprit que ce n'est peut-être pas la meilleure solution même si le résultat semble correct. Ayant un peu de temps, voici le lien de la spécification SVG 1.1 Element "use" .

Objectif:

Pour éviter d'exiger des auteurs qu'ils modifient le document référencé pour ajouter un ID à l'élément racine.

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


Remarques sur SVG 2

La spécification SVG 2 est la prochaine version majeure et prend toujours en charge les fonctionnalités ci-dessus.

3.4. Ordre de rendu

Les éléments en SVG sont positionnés en trois dimensions. En plus de leur position sur les axes x et y de la fenêtre SVG, les éléments SVG sont également positionnés sur l'axe z. La position sur l' axe z définit l'ordre dans lequel ils sont peints .

Le long de l'axe z, les éléments sont regroupés dans des contextes d'empilement.

3.4.1. Établir un contexte d'empilement en SVG

...

Les contextes d'empilement sont des outils conceptuels utilisés pour décrire l'ordre dans lequel les éléments doivent être peints les uns sur les autres lorsque le document est rendu, ...

Maicolpt
la source
Il existe également un ancien brouillon concernant le remplacement de l'ordre de rendu, mais il s'agit d'une fonctionnalité non disponible. Draft Reference
Maicolpt
12
yikes! il n'est pas toujours facile de dessiner les éléments dans l'ordre dans lequel vous voulez les peindre, surtout si les objets sont générés par programme et peuvent apparaître imbriqués (par exemple, il semble que g ne puisse pas contenir a, b, de sorte que a soit inférieur à g frère c mais b est au-dessus)
Michael
@Michael: Dans votre scénario, je vais d'abord essayer de comprendre si vraiment les éléments doivent être regroupés.
Maicolpt
1
Ce 'use xlink: href' est cool et bizarre et parfait pour ce dont j'ai besoin !!
Ian
32

Comme d'autres l'ont dit ici, le z-index est défini par l'ordre d'apparition de l'élément dans le DOM. Si la réorganisation manuelle de votre html n'est pas une option ou serait difficile, vous pouvez utiliser D3 pour réorganiser les groupes / objets SVG.

Utilisez D3 pour mettre à jour l'ordre DOM et imiter la fonctionnalité Z-Index

Mise à jour de l'index Z de l'élément SVG avec D3

Au niveau le plus basique (et si vous n'utilisez pas d'identifiants pour autre chose), vous pouvez utiliser les identifiants d'élément comme substitut pour z-index et réorganiser avec ceux-ci. Au-delà de cela, vous pouvez à peu près laisser libre cours à votre imagination.

Exemples dans l'extrait de code

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>

L'idée de base est:

  1. Utilisez D3 pour sélectionner les éléments DOM SVG.

    var circles = d3.selectAll('circle')
  2. Créez un tableau d'indices z avec une relation 1: 1 avec vos éléments SVG (que vous souhaitez réorganiser). Les tableaux d'index Z utilisés dans les exemples ci-dessous sont les ID, les positions x et y, les rayons, 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. Ensuite, utilisez D3 pour lier vos z-index à cette sélection.

    circles.data(zOrders[setOrderBy]);
  4. Enfin, appelez D3.sort pour réorganiser les éléments dans le DOM en fonction des données.

    circles.sort(setOrder);

Exemples

entrez la description de l'image ici

  • Vous pouvez empiler par ID

entrez la description de l'image ici

  • Avec SVG le plus à gauche sur le dessus

entrez la description de l'image ici

  • Plus petits rayons sur le dessus

entrez la description de l'image ici

  • Ou Spécifiez un tableau pour appliquer z-index pour un ordre spécifique - dans mon exemple de code, le tableau [3,4,1,2,5]déplace / réorganise le 3ème cercle (dans l'ordre HTML d'origine) pour être 1er dans le DOM, 4ème pour être 2ème, 1er pour être 3ème , etc...

Steve Ladavich
la source
Certainement la meilleure réponse ici ... 10/10. Pourquoi est-ce maintenant accepté?
Tigerrrrr
1
@Tigerrrrrr Importer une bibliothèque externe pour faire quelque chose d'aussi simple que de contrôler l'ordre d'empilement est une folie. Pour aggraver les choses, D3 est une bibliothèque particulièrement volumineuse.
iMe le
2
@iMe, bien dit. Bien que ce soit une solution à un problème, il vaut la peine d'être ici; ce n'est pas et ne devrait jamais être la réponse. La seule chose qui devrait jamais remplacer la réponse actuelle serait si une nouvelle spécification sort et que les navigateurs changent. Quiconque cherche à utiliser cette réponse, n'importez pas tout D3, importez simplement les modules dont vous avez besoin. N'IMPORTEZ PAS TOUT LE D3 uniquement pour cela.
Steve Ladavich le
31

Essayez d'inverser #oneet #two. Jetez un œil à ce violon: http://jsfiddle.net/hu2pk/3/

Update

En SVG, le z-index est défini par l'ordre dans lequel l'élément apparaît dans le document . Vous pouvez également consulter cette page si vous le souhaitez: https://stackoverflow.com/a/482147/1932751

Lucas Willems
la source
1
Merci mais j'ai besoin de l'élément basé sur la valeur z-index.
Karthi Keyan
D'accord. Et vous voulez que #one soit sur #deux ou l'inverse?
Lucas Willems
ya si j'ai dit la valeur de z-index comme -1 pour #one signifie qu'il s'affichera au niveau supérieur.
Karthi Keyan
10
Il n'y a aucune propriété z-index dans aucune des spécifications SVG. La seule façon de définir les éléments qui apparaissent en haut et ceux qui apparaissent en bas est d'utiliser l'ordre DOM
nicholaswmin
9
d3.selection.prototype.moveToFront = function() { return this.each(function() { this.parentNode.appendChild(this); }); };Et puis vous pouvez dire selection.moveToFront()via stackoverflow.com/questions/14167863
...
21

Vous pouvez utiliser 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>

Le cercle vert apparaît en haut.
jsFiddle

Jose Rui Santos
la source
4
Cela attire-t-il # un deux fois?
mareoraft
@mareoraft Oui, #oneest dessiné deux fois. Mais si vous le souhaitez, vous pouvez masquer la première instance via CSS. usea le même effet que le clonage de l'élément DOM référencé
Jose Rui Santos
+1 car il ne nécessite pas de javascript mais -1 car vous pouvez également changer l'ordre de <g>lui - même lors du changement de DOM avant de charger de toute façon.
Hafenkranich
14

Comme discuté, les svgs sont rendus dans l'ordre et ne prennent pas en compte le z-index (pour l'instant). Peut-être simplement envoyer l'élément spécifique au bas de son parent afin qu'il soit rendu en dernier.

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

A travaillé pour moi.

Mettre à jour

Si vous avez un SVG imbriqué, contenant des groupes, vous devrez retirer l'élément de son nœud parent.

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

Une fonctionnalité intéressante des SVG est que chaque élément contient son emplacement quel que soit le groupe dans lequel il est imbriqué: +1:

bumsoverboard
la source
salut, cela a fonctionné pour moi, mais quel serait l'équivalent de «ramener au fond»? Merci
gavin le
Les éléments @gavin SVG sont dessinés dans l'ordre de haut en bas. Pour mettre un élément par-dessus, nous l'ajoutons () afin qu'il soit le dernier élément. A l'inverse, si nous voulons qu'un élément soit envoyé en bas, nous le plaçons comme premier élément par prepend (). function bringToBottomofSVG (targetElement) {let parent = targetElement.ownerSVGElement; parent.prepend (targetElement); }
bumsoverboard
13

En utilisant D3:

Si vous souhaitez réinsérer chaque élément sélectionné, dans l'ordre, en tant que dernier enfant de son parent.

selection.raise()
Diso
la source
5
selection.raise()est nouveau dans D3 à partir de la v4.
tephyr
9

Il n'y a pas d'index z pour les svgs. Mais svg détermine lesquels de vos éléments sont les plus élevés par leur position dans le DOM. Ainsi, vous pouvez supprimer l'objet et le placer à la fin du svg, ce qui en fait l'élément "dernier rendu". Celui-ci est ensuite rendu "le plus haut" visuellement.


Utilisation de jQuery:

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

usage:

moveUp($('#myTopElement'));

En utilisant D3.js:

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

usage:

myTopElement.moveUp();

Hafenkranich
la source
son 2019 maintenant, est-ce toujours vrai? Alors que SVG 2.0 a été adopté dans les navigateurs modernes?
Andrew S
4

Les solutions propres, rapides et faciles affichées à la date de cette réponse ne sont pas satisfaisantes. Ils sont construits sur l'affirmation erronée selon laquelle les documents SVG manquent d'ordre z. Les bibliothèques ne sont pas non plus nécessaires. Une ligne de code peut effectuer la plupart des opérations pour manipuler l'ordre z des objets ou des groupes d'objets qui pourraient être nécessaires dans le développement d'une application qui déplace des objets 2D dans un espace xyz.

L'ordre Z existe définitivement dans les fragments de document SVG

Ce qu'on appelle un fragment de document SVG est une arborescence d'éléments dérivés du type de nœud de base SVGElement. Le nœud racine d'un fragment de document SVG est un SVGSVGElement, qui correspond à une balise HTML5 <svg> . Le SVGGElement correspond à la balise <g> et permet d'agréger les enfants.

Avoir un attribut z-index sur le SVGElement comme dans CSS annulerait le modèle de rendu SVG. Les sections 3.3 et 3.4 de la recommandation W3C SVG v1.1 2ème édition indiquent que les fragments de document SVG (arbres des descendants d'un SVGSVGElement) sont rendus en utilisant ce que l'on appelle une première recherche approfondie de l'arbre . Ce schéma est d'ordre az dans tous les sens du terme.

L'ordre Z est en fait un raccourci de vision par ordinateur pour éviter le besoin d'un véritable rendu 3D avec les complexités et les exigences informatiques du lancer de rayons. L'équation linéaire de l'indice z implicite des éléments dans un fragment de document SVG.

z-index = z-index_of_svg_tag + depth_first_tree_index / tree_node_qty

Ceci est important car si vous voulez déplacer un cercle qui était en dessous d'un carré au-dessus, vous insérez simplement le carré avant le cercle. Cela peut être fait facilement en JavaScript.

Méthodes de soutien

Les instances de SVGElement ont deux méthodes qui prennent en charge une manipulation simple et facile de l'ordre z.

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

La bonne réponse qui ne crée pas de gâchis

Étant donné que le SVGGElement ( balise <g> ) peut être supprimé et inséré aussi facilement qu'un SVGCircleElement ou toute autre forme, les couches d'image typiques des produits Adobe et d'autres outils graphiques peuvent être implémentées facilement à l'aide de SVGGElement. Ce JavaScript est essentiellement une commande Déplacer ci-dessous .

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

Si la couche d'un robot dessiné en tant qu'enfants de SVGGElement gRobot était avant la porte dessinée en tant qu'enfants de SVGGElement gDoorway, le robot est maintenant derrière la porte car l'ordre z de la porte est maintenant un plus l'ordre z du robot.

Une commande Déplacer au-dessus est presque aussi simple.

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

Pensez simplement à a = a et b = b pour vous en souvenir.

insert after = move above
insert before = move below

Laisser le DOM dans un état cohérent avec la vue

La raison pour laquelle cette réponse est correcte est qu'elle est minimale et complète et, comme les composants internes des produits Adobe ou d'autres éditeurs graphiques bien conçus, laisse la représentation interne dans un état cohérent avec la vue créée par le rendu.

Approche alternative mais limitée

Une autre approche couramment utilisée consiste à utiliser CSS z-index en conjonction avec plusieurs fragments de document SVG (balises SVG) avec des arrière-plans principalement transparents dans tous sauf celui du bas. Encore une fois, cela va à l'encontre de l'élégance du modèle de rendu SVG, ce qui rend difficile le déplacement des objets vers le haut ou vers le bas dans l'ordre z.


REMARQUES:

  1. ( https://www.w3.org/TR/SVG/render.html v 1.1, 2e édition, 16 août 2011)

    3.3 Ordre de rendu Les éléments d'un fragment de document SVG ont un ordre de dessin implicite, les premiers éléments du fragment de document SVG étant "peints" en premier. Les éléments suivants sont peints au-dessus des éléments déjà peints.

    3.4 Comment les groupes sont rendus Les éléments de regroupement tels que l'élément «g» (voir les éléments conteneurs) ont pour effet de produire un canevas séparé temporaire initialisé en noir transparent sur lequel les éléments enfants sont peints. Une fois le groupe terminé, tous les effets de filtre spécifiés pour le groupe sont appliqués pour créer un canevas temporaire modifié. Le canevas temporaire modifié est composé en arrière-plan, en tenant compte des paramètres de masquage et d'opacité au niveau du groupe sur le groupe.

Douglas Daseeco
la source
4

Nous avons déjà 2019 et z-indexn'est toujours pas pris en charge en SVG.

Vous pouvez voir sur le site SVG2 support dans Mozilla que l'état pour z-index- Non implémenté .

Vous pouvez également voir sur le site le bogue 360148 "Supporte la propriété 'z-index' sur les éléments SVG" (rapporté: il y a 12 ans).

Mais vous avez 3 possibilités en SVG pour le paramétrer:

  1. Avec element.appendChild(aChild);
  2. Avec parentNode.insertBefore(newNode, referenceNode);
  3. Avec targetElement.insertAdjacentElement(positionStr, newElement);(pas de support dans IE pour SVG)

Exemple de démonstration interactive

Avec toutes ces 3 fonctions.

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
la source
2

Poussez l'élément SVG pour durer, de sorte que son z-index soit en haut. En SVG, il n'y a pas de propriété appelée z-index. essayez ci-dessous javascript pour amener l'élément en haut.

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

Cible: est un élément pour lequel nous devons l'amener au sommet svg: est le conteneur d'éléments

Vaseem AbdulSamad
la source
0

c'est facile à faire:

  1. cloner vos articles
  2. trier les éléments clonés
  3. remplacer les éléments par clonés

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