Jak używać indeksu Z w elementach SVG?


154

Używam kręgów SVG w moim projekcie w ten sposób,

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

Używam indeksu Z w gtagu, aby pokazać elementy jako pierwsze. W moim projekcie muszę użyć tylko wartości z-index, ale nie mogę użyć z-index do moich elementów svg. Dużo szukałem w Google, ale względnie niczego nie znalazłem. Więc proszę, pomóż mi używać indeksu Z w moim pliku SVG.

Oto DEMO.


Odpowiedzi:


163

Specyfikacja

W specyfikacji SVG w wersji 1.1 kolejność renderowania jest oparta na kolejności dokumentów:

first element -> "painted" first

Odniesienie do SVG 1.1. Specyfikacja

3.3 Kolejność renderowania

Elementy we fragmencie dokumentu SVG mają niejawną kolejność rysowania, przy czym pierwsze elementy we fragmencie dokumentu SVG są „malowane” jako pierwsze . Kolejne elementy malujemy na wcześniej pomalowanych elementach.


Rozwiązanie (czystsze - szybsze)

Jako ostatni obiekt do narysowania należy umieścić zielone kółko. Więc zamień te dwa elementy.

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

Tutaj widelec twojego jsFiddle .

Rozwiązanie (alternatywne)

Znacznik usez atrybutem xlink:hrefi jako wartość identyfikator elementu. Pamiętaj, że może to nie być najlepsze rozwiązanie, nawet jeśli wynik wydaje się dobry. Mając trochę czasu, tutaj link do specyfikacji SVG 1.1 "użyj" Elementu .

Cel, powód:

Aby uniknąć wymagania od autorów modyfikacji dokumentu, do którego się odwołuje, w celu dodania identyfikatora do elementu głównego.

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


Uwagi dotyczące SVG 2

Specyfikacja SVG 2 jest następną główną wersją i nadal obsługuje powyższe funkcje.

3.4. Kolejność renderowania

Elementy w SVG są rozmieszczone w trzech wymiarach. Oprócz ich położenia na osi X i Y rzutni SVG, elementy SVG są również pozycjonowane na osi z. Pozycja na osi Z określa kolejność ich malowania .

Wzdłuż osi z elementy są pogrupowane w konteksty układania.

3.4.1. Ustalenie kontekstu stosowego w SVG

...

Konteksty układania to narzędzia koncepcyjne używane do opisywania kolejności, w jakiej elementy muszą być malowane jeden na drugim podczas renderowania dokumentu ...


Istnieje również stara wersja robocza dotycząca zastępowania kolejności renderowania, ale jest to funkcja niedostępna. Odniesienie do projektu
Maicolpt,

12
yikes! nie zawsze jest łatwo narysować elementy w kolejności, w jakiej chcesz je pomalować, zwłaszcza jeśli obiekty są generowane programowo i mogą wyglądać na zagnieżdżone (np. wydaje się, że g nie może zawierać a, b, tak że a jest poniżej g rodzeństwa c, ale b jest nad nim)
Michael

@Michael: W twoim scenariuszu najpierw spróbuję zrozumieć, czy naprawdę elementy muszą być pogrupowane.
Maicolpt

1
To „use xlink: href” jest fajne, dziwne i idealne do tego, czego potrzebuję !!
Ian

32

Jak powiedzieli inni tutaj, indeks z jest definiowany przez kolejność, w jakiej element pojawia się w DOM. Jeśli ręczna zmiana kolejności HTML nie wchodzi w grę lub byłaby trudna, możesz użyć D3, aby zmienić kolejność grup / obiektów SVG.

Użyj D3, aby zaktualizować kolejność DOM i naśladować funkcję indeksu Z.

Aktualizacja indeksu Z elementu SVG za pomocą D3

Na najbardziej podstawowym poziomie (i jeśli nie używasz identyfikatorów do niczego innego), możesz użyć identyfikatorów elementów jako zastępczych dla indeksu z i zmienić ich kolejność. Poza tym możesz dać upust swojej wyobraźni.

Przykłady we fragmencie kodu

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>

Podstawowa idea to:

  1. Użyj D3, aby wybrać elementy SVG DOM.

    var circles = d3.selectAll('circle')
  2. Utwórz tablicę z-indeksów w relacji 1: 1 z elementami SVG (które chcesz zmienić). Tablice indeksu Z użyte w poniższych przykładach to identyfikatory, pozycja x i y, promienie itp.

    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. Następnie użyj D3, aby powiązać indeksy z z tym wyborem.

    circles.data(zOrders[setOrderBy]);
  4. Na koniec wywołaj D3.sort, aby zmienić kolejność elementów w DOM na podstawie danych.

    circles.sort(setOrder);

Przykłady

wprowadź opis obrazu tutaj

  • Możesz układać według identyfikatora

wprowadź opis obrazu tutaj

  • Z lewym SVG na górze

wprowadź opis obrazu tutaj

  • Najmniejsze promienie na górze

wprowadź opis obrazu tutaj

  • Lub Określ tablicę, aby zastosować indeks z dla określonej kolejności - w moim przykładowym kodzie tablica [3,4,1,2,5]przesuwa / zmienia kolejność trzeciego koła (w oryginalnej kolejności HTML) na 1. miejsce w DOM, 4. na 2., 1. na 3. , i tak dalej...


Zdecydowanie najlepsza odpowiedź tutaj ... 10/10. Dlaczego jest to teraz akceptowane?
Tigerrrrr

1
@Tigerrrrrr Importowanie zewnętrznej biblioteki w celu wykonania czegoś tak prostego, jak kontrolowanie kolejności układania to szaleństwo. Co gorsza, D3 to szczególnie duża biblioteka.
iMe

2
@iMe, dobrze powiedziane. Chociaż jest to rozwiązanie problemu, sprawia, że ​​warto tu być; to nie jest i nigdy nie powinno być, odpowiedź. Jedyną rzeczą, która powinna kiedykolwiek zastąpić obecną odpowiedź, byłoby pojawienie się nowszej specyfikacji i zmiana przeglądarki. Każdy, kto chce skorzystać z tej odpowiedzi, nie importuje całego D3, wystarczy zaimportować potrzebne moduły. NIE IMPORTUJ CAŁEGO D3 tylko w tym celu.
Steve Ladavich

31

Spróbuj odwrócić #onei #two. Spójrz na to skrzypce: http://jsfiddle.net/hu2pk/3/

Update

W SVG indeks z jest definiowany przez kolejność, w jakiej element pojawia się w dokumencie . Możesz też zajrzeć na tę stronę, jeśli chcesz: https://stackoverflow.com/a/482147/1932751


1
Dzięki, ale potrzebuję elementu opartego na wartości z-index.
Karthi Keyan

Dobrze. I chcesz, żeby #one było na #two czy odwrotnie?
Lucas Willems

ya, jeśli powiedziałem, że wartość indeksu z wynosi -1 dla #one, oznacza to, że będzie wyświetlany na najwyższym poziomie.
Karthi Keyan

10
W żadnej specyfikacji SVG nie ma właściwości z-index. Jedynym sposobem na zdefiniowanie, które elementy pojawiają się na górze, a które na dole, jest użycie kolejności DOM
nicholaswmin.

9
d3.selection.prototype.moveToFront = function() { return this.each(function() { this.parentNode.appendChild(this); }); };A potem możesz powiedzieć selection.moveToFront()przez stackoverflow.com/questions/14167863/ ...
mb21

21

Możesz użyć 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>

Na górze pojawi się zielone kółko.
jsFiddle


4
Czy to losuje # jeden dwa razy?
mareoraft

@mareoraft Tak, #onejest rysowany dwukrotnie. Ale jeśli chcesz, możesz ukryć pierwszą instancję za pomocą CSS. usema taki sam efekt jak klonowanie wspomnianego elementu DOM
Jose Rui Santos

+1, ponieważ nie potrzebuje javascript, ale -1, ponieważ możesz również zmienić kolejność <g>samego siebie podczas zmiany DOM przed załadowaniem i tak.
Hafenkranich

14

Jak już wspomniano, svg renderują się w kolejności i nie uwzględniają z-index (na razie). Może po prostu wyślij określony element na spód jego rodzica, aby był renderowany jako ostatni.

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

Pracował dla mnie.

Aktualizacja

Jeśli masz zagnieżdżony plik SVG zawierający grupy, musisz wyprowadzić element z jego węzła nadrzędnego.

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

Fajną cechą SVG jest to, że każdy element zawiera swoją lokalizację niezależnie od tego, w jakiej grupie jest zagnieżdżony: +1:


Cześć, to zadziałało, ale jaki byłby odpowiednik „sprowadzenia na dno”? Dzięki
gavin

Elementy @gavin SVG są rysowane w kolejności od góry do dołu. Aby umieścić element na górze, dodajemy () go tak, aby był ostatnim elementem. I odwrotnie, jeśli chcemy, aby element został wysłany na dół, umieszczamy go jako pierwszy element za pomocą funkcji prepend (). function bringToBottomofSVG (targetElement) {niech rodzic = targetElement.ownerSVGElement; parent.prepend (targetElement); }
bumsoverboard

13

Korzystanie z D3:

Jeśli chcesz ponownie wstawić każdy wybrany element, po kolei, jako ostatnie dziecko swojego rodzica.

selection.raise()

5
selection.raise()jest nowy w D3 od v4.
tephyr

9

Nie ma indeksu Z dla plików SVG. Ale svg określa, które z twoich elementów znajdują się najwyżej według ich pozycji w DOM. W ten sposób możesz usunąć obiekt i umieścić go na końcu pliku svg, czyniąc go „ostatnim renderowanym” elementem. Ten jest następnie wizualnie renderowany jako „najwyższy”.


Korzystanie z jQuery:

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

stosowanie:

moveUp($('#myTopElement'));

Korzystanie z D3.js:

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

stosowanie:

myTopElement.moveUp();


teraz jest 2019, czy to nadal prawda? Ponieważ SVG 2.0 zyskało popularność w nowoczesnych przeglądarkach?
Andrew S



4

Czyste, szybkie i łatwe rozwiązania opublikowane na dzień udzielenia tej odpowiedzi są niezadowalające. Są one zbudowane na błędnym stwierdzeniu, że dokumenty SVG nie mają kolejności z. Biblioteki też nie są potrzebne. Jedna linia kodu może wykonać większość operacji, aby manipulować kolejnością obiektów lub grup obiektów z, które mogą być wymagane przy tworzeniu aplikacji, która przenosi obiekty 2D w przestrzeni xyz.

Porządek Z na pewno istnieje we fragmentach dokumentów SVG

To, co nazywamy fragmentem dokumentu SVG, jest drzewem elementów pochodzących z węzła podstawowego typu SVGElement. Węzeł główny fragmentu dokumentu SVG to SVGSVGElement, który odpowiada znacznikowi HTML5 <svg> . SVGGElement odpowiada znacznikowi <g> i umożliwia agregację elementów potomnych.

Posiadanie atrybutu z-index na SVGElement, tak jak w CSS, pokonałoby model renderowania SVG. Sekcje 3.3 i 3.4 W3C SVG Zalecenie v1.1 2nd Edition stwierdzają, że fragmenty dokumentów SVG (drzewa potomstwa z SVGSVGElement) są renderowane przy użyciu tak zwanego przeszukiwania w głąb drzewa . Ten schemat jest porządkiem az w każdym znaczeniu tego słowa.

Porządek Z jest w rzeczywistości skrótem do wizji komputerowej, który pozwala uniknąć prawdziwego renderowania 3D przy złożoności i wymaganiach obliczeniowych związanych ze śledzeniem promieni. Równanie liniowe dla niejawnego indeksu Z elementów we fragmencie dokumentu SVG.

z-index = z-index_of_svg_tag + depth_first_tree_index / tree_node_qty

Jest to ważne, ponieważ jeśli chcesz przenieść koło, które znajdowało się poniżej kwadratu, na nad nim, po prostu wstaw kwadrat przed okręgiem. Można to łatwo zrobić w JavaScript.

Metody wspomagające

Instancje SVGElement mają dwie metody, które obsługują prostą i łatwą manipulację porządkiem z.

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

Prawidłowa odpowiedź, która nie tworzy bałaganu

Ponieważ SVGGElement ( tag <g> ) można usunąć i wstawić równie łatwo jak SVGCircleElement lub jakikolwiek inny kształt, warstwy obrazu typowe dla produktów Adobe i innych narzędzi graficznych można z łatwością zaimplementować za pomocą SVGGElement. Ten JavaScript jest w zasadzie poleceniem Przesuń poniżej .

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

Jeśli warstwa robota narysowanego jako elementy podrzędne SVGGElement gRobot była przed drzwiami narysowana jako elementy podrzędne SVGGElement gDoorway, robot znajduje się teraz za drzwiami, ponieważ kolejność z drzwi wynosi teraz jeden plus kolejność z robota.

Move Przede polecenia jest niemal tak proste.

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

Pomyśl tylko a = a i b = b, żeby o tym pamiętać.

insert after = move above
insert before = move below

Pozostawienie DOM w stanie zgodnym z poglądem

Powodem, dla którego ta odpowiedź jest poprawna, jest to, że jest ona minimalna i kompletna oraz, podobnie jak elementy wewnętrzne produktów Adobe lub innych dobrze zaprojektowanych edytorów graficznych, pozostawia wewnętrzną reprezentację w stanie zgodnym z widokiem utworzonym przez renderowanie.

Podejście alternatywne, ale ograniczone

Innym powszechnie stosowanym podejściem jest użycie indeksu z CSS w połączeniu z wieloma fragmentami dokumentów SVG (tagami SVG) z przeważnie przezroczystymi tłami we wszystkich poza dolnym. Ponownie, podważa to elegancję modelu renderowania SVG, utrudniając przesuwanie obiektów w górę lub w dół w kolejności z.


UWAGI:

  1. ( https://www.w3.org/TR/SVG/render.html v 1.1, wydanie drugie, 16 sierpnia 2011)

    3.3 Kolejność renderowania elementów we fragmencie dokumentu SVG ma niejawną kolejność rysowania, przy czym pierwsze elementy we fragmencie dokumentu SVG są „malowane” jako pierwsze. Kolejne elementy malujemy na wcześniej pomalowanych elementach.

    3.4 Jak renderowane są grupy Grupowanie elementów, takich jak element „g” (zobacz elementy kontenera), skutkuje utworzeniem tymczasowej oddzielnej kanwy zainicjowanej w przezroczystą czerń, na którą malowane są elementy potomne. Po zakończeniu tworzenia grupy wszystkie efekty filtrów określone dla grupy są stosowane w celu utworzenia zmodyfikowanej tymczasowej kanwy. Zmodyfikowana tymczasowa kanwa jest umieszczana w tle, z uwzględnieniem wszelkich ustawień maskowania i krycia na poziomie grupy w grupie.


4

Mamy już 2019 rok i z-indexnadal nie jest obsługiwany w SVG.

Możesz zobaczyć na stronie wsparcie SVG2 w Mozilli , dla którego stan z-index- Nie zaimplementowano .

Możesz także zobaczyć na stronie Bug 360148 „Obsługa właściwości 'z-index' w elementach SVG” (zgłoszono: 12 lat temu).

Ale masz 3 możliwości w SVG, aby to ustawić:

  1. Z element.appendChild(aChild);
  2. Z parentNode.insertBefore(newNode, referenceNode);
  3. Z targetElement.insertAdjacentElement(positionStr, newElement);(brak wsparcia w IE dla SVG)

Interaktywny przykład demo

Z tymi wszystkimi 3 funkcjami.

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>


2

Wciśnij element SVG do końca, tak aby jego indeks Z był na górze. W SVG nie ma właściwości zwanej indeksem z. spróbuj poniżej javascript, aby przenieść element na górę.

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

Cel: jest elementem, dla którego musimy go przenieść na górę svg: Czy kontener elementów


0

łatwo to zrobić:

  1. sklonuj swoje przedmioty
  2. sortować sklonowane elementy
  3. zamień elementy na sklonowane

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>

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.