如何在SVG元素中使用Z-index?


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>

我在g标签中使用z-index 来显示第一个元素。在我的项目中,我只需要使用z-index值,但是不能对我的svg元素使用z-index。我已经在Google上搜索了很多,但是没有找到任何相对的东西。因此,请帮助我在我的svg中使用z-index。

这是演示。


Answers:


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

目的:

为了避免要求作者修改引用的文档以向根元素添加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规范是下一个主要版本,仍然支持上述功能。

3.4。渲染顺序

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

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

3.4.1。在SVG中建立堆叠环境

...

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


还有一个关于覆盖渲染顺序的旧草稿,但这是一项不可用的功能。参考草案
Maicolpt

12
y!绘制元素的顺序并不总是那么容易,特别是如果对象是通过编程生成的,并且可能看起来是嵌套的(例如,看起来g不能包含a,b,使得a低于g兄弟c但b在其上方)
迈克尔

@Michael:在您的情况下,首先,我将尝试了解是否必须对元素进行分组。
Maicolpt

1
“使用xlink:href”很酷,很奇怪,非常适合我需要的东西!
伊恩

32

就像其他人所说的那样,z-index是由元素在DOM中出现的顺序定义的。如果无法手动对html进行重新排序或很难解决,则可以使用D3对SVG组/对象进行重新排序。

使用D3更新DOM顺序和模拟Z索引功能

使用D3更新SVG元素Z-索引

在最基本的级别上(如果您没有将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索引数组。以下示例中使用的Z索引数组是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索引绑定到该选择。

    circles.data(zOrders[setOrderBy]);
  4. 最后,调用D3.sort以根据数据对DOM中的元素重新排序。

    circles.sort(setOrder);

例子

在此处输入图片说明

  • 您可以按ID堆叠

在此处输入图片说明

  • 最左侧的SVG位于顶部

在此处输入图片说明

  • 最小半径

在此处输入图片说明

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


绝对是这里最好的答案... 10/10。为什么现在接受?
Tigerrrrr

1
@Tigerrrrrr导入外部库以执行与控制堆栈顺序一样简单的操作是很疯狂的。更糟的是,D3是一个特别大的库。
IME

2
@iMe,好说。虽然这是一个解决问题的办法,使之无愧于这里; 它不是,也绝不应该是,答案。唯一可以替代当前答案的是,如果出现了新的规格并且浏览器发生了变化。希望使用此答案的任何人,不要导入所有D3,只需导入所需的模块即可。请勿仅为此导入所有D3。
史蒂夫·拉达维奇

31

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

Update

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


1
谢谢,但我需要基于z-index值的元素。
Karthi Keyan

好。而您想#one在#two或相反的地方?
卢卡斯·威廉姆斯

是的,如果我将#one的z-index值表示为-1,则表示它将显示在最高级别。
Karthi Keyan

10
在任何SVG规范中都没有z-index属性。定义哪些元素显示在顶部,哪些元素显示在底部的唯一方法是使用DOM顺序
nicholaswmin13年

9
d3.selection.prototype.moveToFront = function() { return this.each(function() { this.parentNode.appendChild(this); }); };然后您可以selection.moveToFront()通过stackoverflow.com/questions/14167863/…
mb21 2014年

21

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


4
这个抽奖两次吗?
mareoraft

@mareoraft是的,#one绘制了两次。但是,如果需要,可以通过CSS隐藏第一个实例。use与克隆引用的DOM元素具有相同的效果
Jose Rui Santos

+1是因为它不需要JavaScript,而-1是因为您还可以<g>在加载之前更改DOM时更改其本身的顺序。
Hafenkranich

14

如前所述,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:


嗨,这对我有用,但是“触到底”的含义是什么?谢谢
加文

@gavin SVG元素从上到下依次绘制。为了将元素放在最上面,我们将其append()用作最后一个元素。相反,如果希望将元素发送到底部,则通过prepend()将其放置为第一个元素。函数BringToBottomofSVG(targetElement){让parent = targetElement.ownerSVGElement; parent.prepend(targetElement); }
bumsoverboard

13

使用D3:

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

selection.raise()

5
selection.raise()从v4开始是D3中的新功能。
tephyr

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


现在是2019年,这仍然是真的吗?随着SVG 2.0在现代浏览器中得到采用?
Andrew S



4

截至本答案发布之日,干净,快速,简便的解决方案并不令人满意。它们是根据SVG文档缺少z顺序的有缺陷的语句构建的。图书馆也不是必需的。一行代码可执行大多数操作,以操纵在xyz空间中移动2D对象的应用程序的开发中可能需要的z个对象或对象组的顺序。

SVG文档片段中绝对存在Z顺序

所谓的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)

不会造成混乱的正确答案

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

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

如果绘制为SVGGElement gRobot的子代的机器人的层位于绘制为SVGGElement gDoorway的子代的门口之前,则该机器人现在位于门口后面,因为门口的z阶现在为1加上该机器人的z阶。

移至上方”命令几乎一样容易。

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,第二版,2011年8月16日)

    3.3渲染顺序SVG文档片段中的元素具有隐式的绘制顺序,SVG文档片段中的第一个元素首先被“绘制”。后续元素被绘制在先前绘制的元素之上。

    3.4如何呈现组诸如“ g”元素(请参见容器元素)之类的分组元素具有产生临时的单独画布的作用,该画布被初始化为透明的黑色,并在上面绘制了子元素。组完成后,将为该组指定的所有滤镜效果都将应用以创建修改后的临时画布。修改后的临时画布会组合到背景中,同时考虑到该组上的任何组级别的遮罩和不透明度设置。


4

我们已经有2019年z-index但SVG仍不支持。

您可以在Mozilla网站上看到SVG2支持状态为z-index未实现

您还可以在站点Bug 360148上看到“在SVG元素上支持'z-index'属性”(报道: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-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>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.