Як використовувати z-індекс у SVG-елементах?


154

Я використовую круги svg у своєму проекті так,

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

І я використовую z-індекс у gтезі, щоб першими показати елементи. У своєму проекті мені потрібно використовувати лише значення z-індексу, але я не можу використовувати z-індекс для своїх svg елементів. Я багато в гугле, але порівняно нічого не знайшов. Тож будь ласка, допоможіть мені використовувати z-індекс у своєму svg.

Ось DEMO.


Відповіді:


163

Специфікація

У специфікації SVG версії 1.1 порядок візуалізації базується на порядку замовлення документа:

first element -> "painted" first

Посилання на SVG 1.1. Специфікація

3.3 Замовлення на надання

Елементи в фрагменті документа SVG мають неявний порядок малювання, причому перші елементи фрагмента документа SVG отримують "пофарбовані" спочатку . Наступні елементи фарбуються поверх попередньо пофарбованих елементів.


Розчин (швидше очищення)

Ви повинні поставити зелене коло як останній об’єкт, який потрібно намалювати. Тому поміняйте місцями два елементи.

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

Ось вилка вашого jsFiddle .

Рішення (альтернативне)

Тег useз атрибутом xlink:hrefі як значення ідентифікатора елемента. Майте на увазі, що це може бути не найкращим рішенням, навіть якщо результат здається прекрасним. Потрапивши трохи часу, тут посилання специфікації SVG 1.1 "use" Element .

Призначення:

Щоб уникнути того, щоб вимагати від авторів модифікувати посилається документ, щоб додати ідентифікатор до кореневого елемента.

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


Примітки до SVG 2

Специфікація SVG 2 - це наступний головний реліз і все ще підтримує вищевказані функції.

3.4. Замовлення на надання

Елементи в SVG розташовані в трьох вимірах. Крім їх положення на осі x і y вікна перегляду SVG, елементи SVG також розташовані на осі z. Положення на осі z визначає порядок їх фарбування .

Вздовж осі z елементи групуються в контексти укладання.

3.4.1. Встановлення контексту укладання у SVG

...

Контексти укладання - це концептуальні інструменти, що використовуються для опису порядку, в якому елементи повинні бути пофарбовані один над одним, коли документ надається, ...


Існує також старий проект щодо зміни порядку замовлення, але ця функція недоступна. Проект посилання
Maicolpt

12
поступається! малювати елементи не завжди легко в тому порядку, в якому ви хочете їх намалювати, особливо якщо об'єкти генеруються програмно і можуть виглядати вкладеними (наприклад, здається, що g не може містити a, b, таким чином, що a знаходиться нижче g братів c, але b над цим)
Майкл

@Michael: У твоєму сценарії спершу я спробую зрозуміти, чи дійсно елементи потрібно згрупувати.
Maicolpt

1
Це "використання xlink: href" - це круто і дивно і ідеально підходить для того, що мені потрібно !!
Ян

32

Як вже говорили інші, z-індекс визначається порядком, коли елемент з'являється в DOM. Якщо вручну упорядкувати ваш HTML не є можливим варіантом або буде важко, ви можете використовувати D3 для впорядкування груп / об’єктів SVG.

Використовуйте D3 для оновлення порядку замовлення DOM та функцій імітації Z-індексу

Оновлення ZG-індексу SVG-елемента за допомогою D3

На самому базовому рівні (і якщо ви не використовуєте ідентифікатори ні для чого іншого), ви можете використовувати ідентифікатори елементів як резервну частину z-індексу та впорядкувати їх. Крім того, ви можете майже дозволити своїй фантазії розігратися.

Приклади у фрагменті коду

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>

Основна ідея:

  1. Використовуйте D3 для вибору елементів SVG DOM.

    var circles = d3.selectAll('circle')
  2. Створіть деякий масив z-індексів із співвідношенням 1: 1 зі своїми елементами SVG (які потрібно змінити впорядкування). Z-індексні масиви, які використовуються у наведених нижче прикладах, - це ідентифікатори, положення x & y, радіуси тощо.

    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. Потім використовуйте D3, щоб прив'язати свої z-індекси до цього вибору.

    circles.data(zOrders[setOrderBy]);
  4. Нарешті, зателефонуйте D3.sort, щоб змінити порядок елементів у DOM на основі даних.

    circles.sort(setOrder);

Приклади

введіть тут опис зображення

  • Ви можете складати ідентифікатор

введіть тут опис зображення

  • З крайнім лівим SVG вгорі

введіть тут опис зображення

  • Найменші радіуси зверху

введіть тут опис зображення

  • Або Вкажіть масив, щоб застосувати z-індекс для конкретного впорядкування - у моєму прикладі коду масив [3,4,1,2,5]переміщує / переробляє 3-е коло (у вихідному порядку HTML), щоб воно було 1-м у DOM, 4-е - 2-м, 1-е - 3-м. , і так далі...


Однозначно найкраща відповідь тут ... 10/10. Чому це зараз прийнято?
Tigerrrrr

1
@Tigerrrrrr Імпорт зовнішньої бібліотеки, щоб зробити щось настільки просто, як керувати порядком укладання, - це божевілля. Щоб зробити це ще гірше, D3 - це особливо велика бібліотека.
iMe

2
@iMe, добре сказано. Хоча це рішення проблеми, що робить його гідним бути тут; це не так , і ніколи не повинно бути, відповідь. Єдине, що коли-небудь повинно замінити поточну відповідь, було б, якщо з’явиться новіша специфікація та зміни браузерів. Усі, хто хоче скористатися цією відповіддю, не імпортуйте всі D3, а просто імпортуйте потрібні модулі. НЕ ІМПОРТУЙТЕ ВСЕ Д3 тільки для цього.
Стів Ладавич

31

Спробуйте перевернути #oneі #two. Погляньте на цю загадку: http://jsfiddle.net/hu2pk/3/

Update

У SVG z-індекс визначається порядком, який елемент відображається в документі . Ви також можете переглянути цю сторінку, якщо хочете: https://stackoverflow.com/a/482147/1932751


1
Дякую, але мені потрібен елемент, заснований на значенні z-індексу.
Karthi Keyan

Гаразд. І ви хочете, щоб #one на #two чи навпаки?
Лукас Віллемс

Так, якщо я сказав значення z-індексу як -1 для #one, це означає, що воно відображатиметься на верхньому рівні.
Karthi Keyan

10
В жодній зі специфікацій SVG немає властивості z-index. Єдиний спосіб визначити, які елементи відображаються вгорі, а які з’являються внизу - за допомогою замовлення DOM
nicholaswmin

9
d3.selection.prototype.moveToFront = function() { return this.each(function() { this.parentNode.appendChild(this); }); };І тоді ви можете сказати selection.moveToFront()по stackoverflow.com/questions/14167863 / ...
MB21

21

Можна використовувати використання .

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

Зверху з’являється зелене коло.
jsFiddle


4
Це малює #one двічі?
мареофт

@mareoraft Так, #oneмалюється двічі. Але якщо хочете, ви можете приховати перший екземпляр через CSS. useмає такий же ефект, як і клонування згаданого елемента DOM
Хосе Руй Сантос

+1 тому, що йому не потрібен JavaScript, але -1, тому що ви також можете змінити порядок <g>себе при зміні DOM перед завантаженням.
Хафенкраніч

14

Як обговорювалося, svgs надають порядок і не враховують z-індекс (поки що). Можливо, просто надішліть певний елемент у нижній частині свого батьківського, щоб він став останнім.

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

Працювали для мене.

Оновлення

Якщо у вас є вкладений SVG, що містить групи, вам потрібно буде вивести елемент із його parentNode.

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

Приємною особливістю SVG є те, що кожен елемент містить своє місцезнаходження незалежно від того, в якій групі він вкладений: +1:


привіт, це працювало для мене, але що було б "еквівалентом"? Спасибі
gavin

Елементи SVG @gavin малюються в порядку зверху вниз. Щоб покласти елемент зверху, додаємо () його так, щоб він був останнім елементом. І навпаки, якщо ми хочемо, щоб елемент, надісланий донизу, розміщуємо його як перший елемент за допомогою prepend (). функція doneToBottomofSVG (targetElement) {нехай батьків = targetElement.ownerSVGElement; parent.prepend (targetElement); }
bumsoverboard

13

Використання D3:

Якщо ви хочете повторно вставити кожен вибраний елемент, по порядку, як остання дочірка його батьків.

selection.raise()

5
selection.raise()є новим у D3 станом на v4.
тефір

9

Не існує z-індексу для svgs. Але svg визначає, хто з ваших елементів є найвищим за їх місцем у DOM. Таким чином, ви можете видалити Об'єкт і розмістити його в кінці svg, зробивши його елементом "останній наданий". Потім це візуально відображається "найвищим".


Використання jQuery:

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

використання:

moveUp($('#myTopElement'));

Використання D3.js:

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

використання:

myTopElement.moveUp();


його 2019 рік, це все-таки правда? Як SVG 2.0 отримав прийняття у сучасних браузерах?
Andrew S



4

Чисті, швидкі та прості рішення, розміщені на дату цієї відповіді, незадовільні. Вони побудовані на хибному твердженні про відсутність порядку SVG-документах. Бібліотеки теж не потрібні. Один рядок коду може виконувати більшість операцій, щоб керувати порядком z об'єктів або груп об'єктів, які можуть знадобитися при розробці програми, яка переміщує 2D-об'єкти навколо в ксизовому просторі.

Z Замовлення напевно існує у фрагментах документа SVG

Фрагмент документа SVG називається деревом елементів, похідним від базового вузла типу SVGElement. Кореневий вузол фрагмента документа SVG - це SVGSVGElement, який відповідає тегу HTML5 <svg> . SVGGElement відповідає тегу <g> і дозволяє об'єднувати дітей.

Наявність атрибута z-індексу на SVGElement, як у CSS, переможе модель SVG-рендерінга. У розділах 3.3 та 3.4 W3C SVG Рекомендація v1.1 2-го видання зазначено, що фрагменти документа SVG (дерева потомства від SVGSVGElement) відображаються з використанням того, що називається глибиною першого пошуку дерева . Ця схема є порядком у будь-якому сенсі цього терміна.

Порядок Z - це фактично ярлик комп'ютерного зору, щоб уникнути необхідності справжнього 3D-рендерінгу зі складностями та обчислювальними вимогами трасування променів. Лінійне рівняння для неявного z-індексу елементів у фрагменті документа SVG.

z-index = z-index_of_svg_tag + depth_first_tree_index / tree_node_qty

Це важливо, тому що якщо ви хочете перемістити коло, яке було нижче квадрата, над ним, ви просто вставите квадрат перед колом. Це легко зробити в JavaScript.

Методи підтримки

Екземпляри SVGElement мають два методи, які підтримують просту та просту маніпуляцію із замовленням.

  • parent.removeChild (дитина)
  • parent.insertBefore (дитина, дитинаRef)

Правильний відповідь, який не створює безлад

Оскільки SVGGElement ( <g> тег) можна видалити та вставити так само легко, як SVGCircleElement або будь-яку іншу форму, шари зображень, характерні для продуктів Adobe та інших графічних інструментів, можна легко реалізувати за допомогою SVGGElement. Цей JavaScript по суті є командою " Перемістити внизу" .

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

Якщо шар робота, намальований дітьми SVGGElement gRobot, був перед дверима, намальованим як діти SVGGElement gDoorway, робот зараз знаходиться за дверима, оскільки порядок z в дверному отворі тепер один плюс z порядок робота.

Команда « Перемістити вище» майже так само просто.

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

Просто подумайте a = a і b = b, щоб запам'ятати це.

insert after = move above
insert before = move below

Залишаючи DOM у стані, що відповідає поглядам

Причина, що ця відповідь є правильною, полягає в тому, що вона мінімальна і повна, і, як і внутрішні продукти Adobe та інших добре розроблених графічних редакторів, залишає внутрішнє представлення у стані, що відповідає виду, створеному рендерінгом.

Альтернативний, але обмежений підхід

Інший підхід, який зазвичай використовується - це використання z-індексу CSS у поєднанні з декількома фрагментами документів SVG (теги SVG) з переважно прозорими фонами у всіх, окрім нижнього. Знову ж таки, це перемагає елегантність моделі візуалізації SVG, ускладнюючи переміщення об'єктів вгору або вниз у порядку z.


ПРИМІТКИ:

  1. ( https://www.w3.org/TR/SVG/render.html v 1.1, 2-е видання, 16 серпня 2011 р.)

    3.3 Елементи порядку візуалізації у фрагменті документа SVG мають неявний порядок малювання, причому перші елементи фрагмента документа SVG першими «фарбуються». Наступні елементи фарбуються поверх попередньо пофарбованих елементів.

    3.4 Як представлені групи Елементи групування, такі як елемент 'g' (див. Елементи контейнерів), створюють тимчасове окреме полотно, ініційоване на прозорий чорний колір, на якому намальовані дочірні елементи. Після завершення групи всі ефекти фільтру, визначені для групи, застосовуються для створення модифікованого тимчасового полотна. Змінене тимчасове полотно компонується на задньому плані з урахуванням будь-яких параметрів маскування на рівні групи та непрозорості для групи.


4

У нас вже 2019 рік і z-indexдосі його не підтримують у SVG.

Ви можете побачити на сайті підтримку SVG2 в Mozilla, що стан для z-index- Не реалізовано .

Ви також можете побачити на сайті Bug 360148 "Підтримка властивості 'z-index' для елементів SVG" (Повідомлялося: 12 років тому).

Але у SVG у вас є 3 можливості встановити:

  1. З element.appendChild(aChild);
  2. З parentNode.insertBefore(newNode, referenceNode);
  3. З targetElement.insertAdjacentElement(positionStr, newElement);(Немає підтримки в IE для SVG)

Інтерактивний демонстраційний приклад

При всьому цьому 3 функції.

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

Натисніть SVG-елемент до останнього, щоб його z-індекс опинився вгорі. У SVG немає властивості під назвою z-індекс. спробуйте нижче JavaScript, щоб довести елемент до вершини.

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

Мета: це елемент, для якого нам потрібно довести його до верхнього svg: чи є контейнер елементів


0

це легко зробити:

  1. клонувати ваші предмети
  2. сортувати клоновані предмети
  3. замінити елементи клонованими

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>

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.