【问题标题】:How to use z-index in svg elements?如何在 svg 元素中使用 z-index?
【发布时间】:2013-07-21 03:09:20
【问题描述】:

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

这里是DEMO.

【问题讨论】:

标签: javascript jquery svg z-index


【解决方案1】:

规格

在 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 属性(href 对应SVG 2),值是元素的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

...

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

【讨论】:

  • 还有一个关于覆盖渲染顺序的旧草案,但它是一个不可用的功能。 Draft Reference
  • 哎呀!按照您希望它们绘制的顺序绘制元素并不总是那么容易,特别是如果对象是以编程方式生成并且可能出现嵌套(例如,它似乎 g 不能包含 a、b、这样 a 低于 g 兄弟 c 但 b 高于它)
  • @Michael:在您的场景中,首先我会尝试了解是否真的必须对元素进行分组。
  • 'use xlink:href' 很酷很奇怪,非常适合我的需要!!
  • 对于在 2021 年或以后发现此答案的人,在 SVG 2 版本中,xlink:href 已被弃用,取而代之的是 href
【解决方案2】:

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

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

Updating SVG Element Z-Index With D3

在最基本的级别(如果您没有将 ID 用于其他任何事情),您可以使用元素 ID 作为 z-index 的替代品并使用它们重新排序。除此之外,您几乎可以尽情发挥想象力。

代码 sn-p 中的示例

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-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 索引绑定到该选择。

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

    circles.sort(setOrder);
    

示例

  • 您可以按 ID 堆叠

  • 最左边的 SVG 在顶部

  • 顶部的最小半径

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

【讨论】:

  • 绝对是这里的最佳答案... 10/10。为什么现在被接受了?
  • @Tigerrrrrr 导入一个外部库来做一些像控制堆叠顺序这样简单的事情是疯狂的。更糟糕的是,D3 是一个特别大的库。
  • @iMe,说得好。虽然这是对问题的一个解决方案,但值得在这里;它不是,也不应该是, 的答案。唯一应该取代当前答案的就是如果更新的规范出现并且浏览器发生变化。任何想要使用这个答案的人,不要导入所有 D3,只需导入您需要的模块。不要仅仅为此而导入所有 D3。
【解决方案3】:

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

Update

在 SVG 中,z-index 由元素在文档中出现的顺序定义。如果你愿意,你也可以看看这个页面:https://stackoverflow.com/a/482147/1932751

【讨论】:

  • 谢谢,但我需要基于 z-index 值的元素。
  • 好的。你希望#one 在#two 上还是相反?
  • 如果我说 z-index 值为 -1 表示 #one 表示它将显示在顶层。
  • 在任何 SVG 规范中都没有 z-index 属性。定义哪些元素出现在顶部以及哪些元素出现在底部的唯一方法是使用 DOM 排序
  • d3.selection.prototype.moveToFront = function() { return this.each(function() { this.parentNode.appendChild(this); }); }; 然后你可以通过stackoverflow.com/questions/14167863/…selection.moveToFront()
【解决方案4】:

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

【讨论】:

  • 这会抽#one 两次吗?
  • @mareoraft 是的,#one 被绘制了两次。但如果你愿意,你可以通过 CSS 隐藏第一个实例。 use 与克隆引用的 DOM 元素效果相同
  • +1 因为它不需要 javascript 但 -1 因为您也可以在加载之前更改 DOM 时更改 &lt;g&gt; 本身的顺序。
  • 对于在 2021 年或之后发现此答案的人,在 SVG 2 版本中,xlink:href 已被弃用,取而代之的是 href
【解决方案5】:

如前所述,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,您需要将项目从其父节点中取出。

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

SVG 的一个很好的特性是每个元素都包含它的位置,而不管它嵌套在哪个组中:+1:

【讨论】:

  • 嗨,这对我有用,但是“降到最低”的等价物是什么?谢谢
  • @gavin SVG 元素按从上到下的顺序绘制。要将元素放在顶部,我们 append() 它使其成为最后一个元素。相反,如果我们想要将一个元素发送到底部,我们通过 prepend() 将其放置为第一个元素。 function bringToBottomofSVG(targetElement){ let parent = targetElement.ownerSVGElement; parent.prepend(targetElement); }
  • 只有当组没有转换属性时才适用:“..每个元素都包含它的位置,无论它嵌套在哪个组中”
【解决方案6】:

使用 D3:

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

selection.raise()

【讨论】:

  • selection.raise() 在 D3 v4 中是新的。
【解决方案7】:

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 在现代浏览器中的普及?
【解决方案8】:

使用 D3:

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

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

而不是 .append

Adding an element to top of a group element

【讨论】:

    【解决方案9】:

    截至本答案发布之日发布的干净、快速且简单的解决方案并不令人满意。它们是建立在 SVG 文档缺乏 z 顺序的有缺陷的陈述之上的。库也不是必需的。一行代码可以执行大多数操作来操作对象或对象组的 z 顺序,这在开发在 x-y-z 空间中移动 2D 对象的应用程序时可能需要。

    SVG 文档片段中肯定存在 Z 顺序

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

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

    Z 顺序实际上是一种计算机视觉捷径,可避免对真正的 3D 渲染的需求以及光线追踪的复杂性和计算需求。 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( 标签)可以像 SVGCircleElement 或任何其他形状一样轻松地移除和插入,因此可以轻松实现 Adob​​e 产品和其他图形工具的典型图像层使用 SVGGElement。这个 JavaScript 本质上是一个移到下面命令。

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

    如果作为 SVGGElement gRobot 的子级绘制的机器人层在作为 SVGGElement gDoorway 的子级绘制的门口之前,则机器人现在位于门口之后,因为门口的 z 顺序现在是机器人的 z 顺序的 1 .

    移到上方命令几乎同样简单。

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

    想想 a=a 和 b=b 就可以记住这一点。

    insert after = move above
    insert before = move below
    

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

    此答案正确的原因是因为它最小且完整,并且与 Adob​​e 产品或其他精心设计的图形编辑器的内部一样,使内部表示处于与渲染创建的视图一致的状态。

    替代但有限的方法

    另一种常用的方法是将 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”元素(参见容器元素)具有产生临时 单独的画布初始化为透明黑色到哪个孩子 元素被绘制。组完成后,任何过滤器 应用为组指定的效果来创建修改后的 临时画布。修改后的临时画布合成为 背景,考虑到任何组级遮罩和不透明度 组上的设置。

    【讨论】:

      【解决方案10】:

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

      【讨论】:

        【解决方案11】:

        我们已经 2019 并且 z-index 在 SVG 中仍然不受支持。

        您可以在 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>

        【讨论】:

          【解决方案12】:

          将 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:是元素的容器

          【讨论】:

            【解决方案13】:

            通过变换移动到前面:TranslateZ

            警告:仅适用于 FireFox

            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 160" style="width:160px; height:160px;">
                <g style="transform-style: preserve-3d;">
                    <g id="one" style="transform-style: preserve-3d;">
                        <circle fill="green" cx="100" cy="105" r="20" style="transform:TranslateZ(1px);"></circle>
                    </g>
                    <g id="two" style="transform-style: preserve-3d;">
                        <circle fill="orange" cx="100" cy="95" r="20"></circle>
                    </g>
                </g>
            </svg>

            【讨论】:

            • 这里不适合我。橙色的覆盖绿色的。
            • @krave: 什么浏览器?
            • Chrome 最新版本
            • @krave:你是对的。仅适用于 FireFox。对不起
            【解决方案14】:

            这是有关 z-index 和 SVG 搜索的最高 Google 结果。看完了所有的答案,其中一些非常好,我还是一头雾水。

            所以对于像我这样的新手来说,这是目前的总结,9 年后的 2022 年。

            您不能将 z-index 与 SVG 一起使用。

            在 SVG 中,z-index 由元素在文档中出现的顺序定义。

            如果你想让某些东西出现在顶部,或者更靠近用户,请最后绘制。Source

            SVG 2 可以支持 z-index,但可能永远不会出现

            SVG 2 是实现该功能和其他功能的提议,但它有永远不会向前发展的风险。

            SVG 2 于 2016 年进入候选推荐阶段,并于 2018 年进行了修订,最新草案于 2021 年 6 月 8 日发布。Source

            但是,它没有很多支持,而且很少有人在研究它。 Source 所以不要屏住呼吸等待这个。

            您可以使用 D3,但可能不应该

            D3 一个常用的可视化数据通过绑定 z-index 然后排序来支持 z-index,但它是一个庞大而复杂的库,如果您只想显示某个 SVG,它可能不是最好的选择栈顶。

            【讨论】:

              【解决方案15】:

              很容易做到:

              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>

              【讨论】:

                【解决方案16】:

                use 的一个更好的例子,我最终使用了它。

                <svg>
                  <defs>
                    <circle id="one" fill="green" cx="40" cy="40" r="20" /> 
                    <circle id="two" fill="orange" cx="50" cy="40" r="20"/> 
                  </defs>
                  <use href="#two" />
                  <use href="#one" />
                </svg>
                

                要控制顺序,您可以更改这些use 元素的href 属性值。这对动画很有用。

                感谢defs,圆形元素只绘制一次。

                jsfiddle.net/7msv2w5d

                【讨论】:

                  猜你喜欢
                  • 2012-11-15
                  • 2013-04-26
                  • 2014-07-25
                  • 1970-01-01
                  • 2016-03-04
                  • 2015-09-28
                  • 2021-02-10
                  • 2019-11-17
                  相关资源
                  最近更新 更多