如何在svg元素中使用z-index?

问题描述 投票:127回答:13

我在我的项目中使用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>

我在g标签中使用z-index来显示第一个元素。在我的项目中,我只需要使用z-index值,但我不能将z-index用于我的svg元素。我google了很多,但我找不到任何相对的东西。所以请帮我在我的svg中使用z-index。

这是DEMO.

javascript jquery svg z-index
13个回答
139
投票

规格

在SVG规范1.1版中,呈现顺序基于文档顺序:

first element -> "painted" first

参考SVG 1.1. Specification

3.3 Rendering Order

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,值为元素的id。请记住,即使结果看起来不错,也可能不是最佳解决方案。有一点时间,这里是规范SVG 1.1 "use" Element的链接。

目的:

避免要求作者修改引用的文档以向根元素添加ID。

<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 Specification是下一个主要版本,仍然支持上述功能。

3.4. Rendering order

SVG中的元素以三维方式定位。除了它们在SVG视口的x和y轴上的位置之外,SVG元素也位于z轴上。 z轴上的位置定义了它们的绘制顺序。

沿z轴,元素被分组为堆叠上下文。

3.4.1. Establishing a stacking context in SVG

...

堆叠上下文是用于描述在呈现文档时元素必须一个在另一个上面绘制的顺序的概念工具,...


4
投票

截至本答复日期发布的清晰,快速,简便的解决方案并不令人满意。它们构建在SVG文档缺乏z顺序的错误陈述之上。图书馆也不是必需的。一行代码可以执行大多数操作来操纵在x-y-z空间中移动2D对象的应用程序开发中可能需要的对象或对象组的z顺序。

Z Order绝对存在于SVG文档片段中

所谓的SVG文档片段是从基本节点类型SVGElement派生的元素树。 SVG文档片段的根节点是SVGSVGElement,它对应于HTML5 <svg>标记。 SVGGElement对应<g>标签并允许聚合子项。

像CSS一样在SVGElement上使用z-index属性会破坏SVG渲染模型。 W3C SVG Recommendation v1.1 2nd Edition的第3.3和3.4节规定SVG文档片段(来自SVGSVGElement的后代树)使用所谓的树的深度优先搜索来呈现。该方案在该术语的每个意义上都是z顺序。

Z顺序实际上是一种计算机视觉快捷方式,可以避免对光线跟踪的复杂性和计算需求进行真正的3D渲染。 SVG文档片段中元素的隐式z索引的线性方程。

z-index = z-index_of_svg_tag + depth_first_tree_index / tree_node_qty

这很重要,因为如果要将一个方形下方的圆圈移动到它上方,只需在圆形之前插入方形。这可以在JavaScript中轻松完成。

支持方法

SVGElement实例有两种方法支持简单和简单的z顺序操作。

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

没有造成混乱的正确答案

由于SVGGElement(<g>标签)可以像SVGCircleElement或任何其他形状一样轻松地移除和插入,因此可以使用SVGGElement轻松实现Adobe产品和其他图形工具的典型图像层。这个JavaScript本质上是一个Move Below命令。

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

如果作为SVGGElement gRobot的子项绘制的机器人层位于作为SVGGElement gDoorway的子项绘制的门口之前,则机器人现在位于门口后面,因为门口的z顺序现在加上机器人的z顺序。

Move Above命令几乎一样容易。

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

试想一下a = a和b = b来记住这一点。

insert after = move above
insert before = move below

让DOM处于与视图一致的状态

这个答案是正确的原因是因为它是最小和完整的,并且像Adobe产品的内部或其他精心设计的图形编辑器一样,将内部表示留在与渲染创建的视图一致的状态。

替代但有限的方法

另一种常用的方法是将CSS z-index与多个SVG文档片段(SVG标签)结合使用,除了底层之外的所有部分都具有大多数透明背景。同样,这会破坏SVG渲染模型的优雅,使得难以按z顺序向上或向下移动对象。


笔记:

  1. https://www.w3.org/TR/SVG/render.html v 1.1,第2版,2011年8月16日) 3.3渲染顺序SVG文档片段中的元素具有隐式绘制顺序,SVG文档片段中的第一个元素首先被“绘制”。后续元素绘制在先前绘制的元素之上。 3.4如何渲染组分组元素(如“g”元素(请参阅容器元素))具有生成临时单独画布的效果,该画布初始化为透明黑色,绘制子元素。完成组后,将应用为该组指定的任何过滤器效果以创建已修改的临时画布。修改后的临时画布将合成到背景中,并考虑组中的任何组级别遮罩和不透明度设置。

4
投票

我们已经2019年了,SVG仍然不支持z-index

你可以在网站SVG2 support in Mozilla上看到z-index的状态 - 未实现。

你也可以在网站上看到Bug 360148 "Support the 'z-index' property on SVG elements"(报道: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-index位于顶部。在SVG中,没有称为z-index的属性。尝试下面的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>

28
投票

尝试颠倒#one#two。看看这个小提琴:http://jsfiddle.net/hu2pk/3/

Update

在SVG中,z-index由元素在文档中出现的顺序定义。如果您需要,也可以查看此页面:https://stackoverflow.com/a/482147/1932751


23
投票

正如其他人所说,z-index是由元素在DOM中出现的顺序定义的。如果手动重新排序您的html不是一个选项或者很难,您可以使用D3来重新排序SVG组/对象。

使用D3更新DOM顺序和模拟Z-Index功能

Updating SVG Element Z-Index With D3

在最基本的级别(如果您不使用其他任何ID),您可以使用元素ID作为z-index的替代,并重新排序。除此之外,你几乎可以让你的想象力疯狂。

代码段中的示例

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. 创建一些与您的SVG元素(要重新排序)的1:1关系的z-indices数组。以下示例中使用的Z-index数组是ID,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-indices绑定到该选择。 circles.data(zOrders[setOrderBy]);
  4. 最后,调用D3.sort根据数据重新排序DOM中的元素。 circles.sort(setOrder);

例子

enter image description here

  • 您可以按ID进行堆叠

enter image description here

  • 最左边的SVG在顶部

enter image description here

  • 顶部最小的半径

enter image description here

  • 或指定一个数组以应用特定排序的z-index - 在我的示例代码中,数组[3,4,1,2,5]移动/重新排序第3个圆圈(在原始HTML顺序中)为DOM中的第1个,第4个为第2个,第1个为3,依此类推......


20
投票

你可以使用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>

绿色圆圈显示在顶部。 jsFiddle


9
投票

svgs没有z-index。但是svg确定哪些元素在DOM中的最重要位置。因此,您可以删除Object并将其放在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();


9
投票

如上所述,svgs按顺序呈现,并且不考虑z-index(现在)。也许只是将特定元素发送到其父元素的底部,以便它最后呈现。

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:


8
投票

使用D3:

如果要以相反的顺序将元素添加到数据使用:

.insert('g', ":first-child")

而不是.append

Adding an element to top of a group element


7
投票

使用D3:

如果要按顺序重新插入每个选定元素,作为其父级的最后一个子元素。

selection.raise()

5
投票

另一种解决方案是使用div,它使用zIndex来包含SVG元素。如下:https://stackoverflow.com/a/28904640/4552494

© www.soinside.com 2019 - 2024. All rights reserved.