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

242

我在我的项目中使用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元素。我已经进行了大量的谷歌搜索,但没有找到相关的内容。

请帮助我在我的svg中使用z-index。

这是演示。


18个回答

235

规范

在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 的分支。

解决方案(替代方案)

使用带有属性 xlink:href(对于SVG 2只需使用href)和元素 id 作为值的 use 标签。请记住,即使结果看起来不错,这可能不是最佳解决方案。如果有一点时间,这里是规范的链接SVG 1.1“use”元素

目的:

避免要求作者修改引用文档以添加根元素的 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中建立一个堆叠上下文

...

堆叠上下文是用来描述文档呈现时元素必须按顺序绘制在另一个元素的顶部或底部的概念工具...


1
还有一个旧的草案关于覆盖渲染顺序,但这是一个不可用的功能。草案参考 - Maicolpt
30
哎呀!按照想要的绘制顺序排列元素并不总是容易的,特别是当对象被程序动态生成且可能出现嵌套时(例如,似乎 g 不能包含 a 和 b,其中 a 在 g 的兄弟节点 c 下面但 b 在其上面)。 - Michael
@Michael:在您的情况下,首先我会尝试理解元素是否真的需要分组。 - Maicolpt
1
“use xlink:href” 这个很酷、奇怪,而且正好符合我的需求!! - Ian
3
对于在2021年或之后发现此答案的人,随着SVG 2版本的发布,xlink:href已被弃用,取而代之的是href - G07cha

47

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

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

使用D3更新SVG元素的Z-Index

在最基本的级别上(如果您没有为其他任何事情使用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. Use D3 to select the SVG DOM elements.

    var circles = d3.selectAll('circle')
    
  2. Create some array of z-indices with a 1:1 relationship with your SVG elements (that you want to reorder). Z-index arrays used in the examples below are IDs, x & y position, radii, 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. Then, use D3 to bind your z-indices to that selection.

    circles.data(zOrders[setOrderBy]);
    
  4. Lastly, call D3.sort to reorder the elements in the DOM based on the data.

    circles.sort(setOrder);
    

示例

在这里输入图片描述

  • 您可以按ID进行叠放

在这里输入图片描述

  • 左侧最上方的SVG在顶部

在这里输入图片描述

  • 最小半径在顶部

在这里输入图片描述

  • 或者指定一个数组来应用特定的z-index排序--在我的示例代码中,数组[3,4,1,2,5]将第三个圆(在原始HTML顺序中)移动到DOM中的第一个位置,第四个圆移动到第二个位置,第一个圆移动到第三个位置,等等...


19
将外部库导入来控制层叠顺序这样简单的事情,实在是疯狂。更糟糕的是,D3 是一个特别庞大的库。 - iMe
6
@iMe说得不错。虽然这是解决问题的一个方案,值得在这里被呈现;但它并不应该是唯一的答案。唯一应该取代当前答案的是如果有新的规范出现并且浏览器发生变化。任何想使用这个答案的人,不要导入所有的D3,只需导入你需要的模块。不要仅为了这个而导入所有D3。 - Steve Ladavich

37

1
谢谢,但我需要基于z-index值的元素。 - Karthi Keyan
好的。你想要 #one 在 #two 上面还是相反? - Lucas Willems
如果我将#one的z-index值设为-1,那么它将显示在顶层。 - Karthi Keyan
15
SVG规范中没有z-index属性。唯一确定元素出现在顶部或底部的方法是使用DOM排序。 - nicholaswmin
9
d3.selection.prototype.moveToFront = function() { return this.each(function() { this.parentNode.appendChild(this); }); }; 这段代码的作用是将选中的元素移动到其父元素的最前面。然后你可以使用 selection.moveToFront() 来调用它。这个代码片段来自于 https://dev59.com/8WYq5IYBdhLWcg3w0T1y。 - mb21
现在是2019年,浏览器是否支持SVG 2?另外,根据规范,SVG 1.1支持对组的排序。我们不能将该项添加到组中吗? - Andrew S

23
你可以使用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
这个问题是否会重复绘制 #one 这个元素? - mareoraft
@mareoraft 是的,#one 会被绘制两次。但是如果你想的话,可以通过 CSS 隐藏第一个实例。use 具有与克隆所引用的 DOM 元素相同的效果。 - Jose Rui Santos
2
+1 是因为它不需要 JavaScript,但-1 是因为在加载之前更改 DOM 时,您也可以更改<g>本身的顺序。 - Hafenkranich
对于在2021年或之后发现此答案的人,随着SVG 2版本的发布,“xlink:href”已被弃用,取而代之的是“href”。 - G07cha

20

如所讨论,SVG图形按顺序渲染,不考虑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:


嗨,这对我有用,但“bring to bottom”的等效物是什么?谢谢。 - gavin stanley
@gavin SVG元素从上到下按顺序绘制。要将元素置于顶部,我们使用append()将其添加为最后一个元素。相反,如果我们想要将元素发送到底部,我们通过prepend()将其放置在第一个元素。function bringToBottomofSVG(targetElement){ let parent = targetElement.ownerSVGElement; parent.prepend(targetElement); } - bumsoverboard
只有在组没有转换属性时,这才是正确的:“..每个元素都包含其位置,无论它嵌套在哪个组中。” - akauppi

18

使用D3:

如果您想按顺序将每个已选择的元素重新插入其父元素作为最后一个子元素。

selection.raise()

6
selection.raise()是D3 v4中的新功能,用于将选定元素提升至其同级元素的顶部。 - tephyr

15

使用 D3:

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

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

而不是 .append('g')

在组元素顶部添加元素


11
这是有关z-index和SVG搜索的顶级谷歌结果。阅读了所有答案后,其中一些非常好,但我仍然感到困惑。
因此,对于像我这样的新手,在2022年的当前总结如下。
您无法在SVG中使用z-index。
在SVG中,z-index由元素出现在文档中的顺序定义。
如果您希望某些内容显示在顶部或更接近用户,请最后绘制它或在其前面插入。来源 SVG 2可能支持z-index,但可能永远不会发布。
SVG 2是一个提议实施该功能和其他功能的提案,但有可能永远不会推进。
SVG 2在2016年达到候选建议阶段,并于2018年进行了修订,最新草案于2021年6月8日发布。来源 然而它没有得到很多支持,很少有人在开发。来源所以不要抱着期待等待它。 你可以使用D3,但可能不应该 D3是一个常用于可视化数据的库,通过绑定z-index并进行排序来支持z-index,但它是一个庞大而复杂的库,如果你只想让某个SVG出现在堆栈的顶部,它可能不是最好的选择。

9

SVG图形没有z-index属性。但是,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

6
截至本回答日期发布的干净、快速和简单解决方案都不令人满意。它们建立在错误的前提下,即SVG文档缺乏Z顺序。也不需要使用库。一行代码可以执行大多数操作,以操纵对象或对象组的z顺序,这可能在开发将2D对象移动到x-y-z空间中时是必需的。
在SVG文档片段中,Z顺序绝对存在。
所谓的SVG文档片段是从基本节点类型SVGElement派生的元素树。SVG文档片段的根节点是SVGSVGElement,它对应于HTML5的标记。SVGGElement对应于标记,并允许聚合子元素。
在SVGElement上具有z-index属性,就像CSS一样,会破坏SVG渲染模型。 W3C SVG Recommendation v1.1第二版的3.3和3.4节规定,使用称为“深度优先搜索树”的方案呈现SVG文档片段(从SVGSVGElement产生的后代树)。该方案在各个方面都是Z顺序。
Z顺序实际上是计算机视觉的一种快捷方式,避免了真正的三维渲染所需的射线追踪的复杂性和计算需求。这是SVG文档片段中元素隐式z-index的线性方程。
z-index = z-index_of_svg_tag + depth_first_tree_index / tree_node_qty

这很重要,因为如果您想将一个在正方形下面的圆移动到它上面,只需在圆之前插入正方形即可。这可以在JavaScript中轻松完成。
支持方法
SVGElement实例有两个方法,支持简单易行的z顺序操作。 parent.removeChild(child) parent.insertBefore(child, childRef)
正确答案不会创建混乱
由于SVGGElement(

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接