IT TIP

D3로 SVG 요소 Z- 색인 업데이트

itqueen 2020. 10. 20. 19:01
반응형

D3로 SVG 요소 Z- 색인 업데이트


D3 라이브러리를 사용하여 SVG 요소를 z 순서의 맨 위로 가져 오는 효과적인 방법은 무엇입니까?

내 특정 시나리오의 (a를 추가하여하는 하이라이트 파이 차트 stroke받는 path마우스가 주어진 조각 위에있을 때). 내 차트를 생성하는 코드 블록은 다음과 같습니다.

svg.selectAll("path")
    .data(d)
  .enter().append("path")
    .attr("d", arc)
    .attr("class", "arc")
    .attr("fill", function(d) { return color(d.name); })
    .attr("stroke", "#fff")
    .attr("stroke-width", 0)
    .on("mouseover", function(d) {
        d3.select(this)
            .attr("stroke-width", 2)
            .classed("top", true);
            //.style("z-index", 1);
    })
    .on("mouseout", function(d) {
        d3.select(this)
            .attr("stroke-width", 0)
            .classed("top", false);
            //.style("z-index", -1);
    });

몇 가지 옵션을 시도했지만 지금까지 운이 없습니다. 둘 다 사용 style("z-index")하고 호출 classed해도 작동하지 않았습니다.

"상위"클래스는 내 CSS에서 다음과 같이 정의됩니다.

.top {
    fill: red;
    z-index: 100;
}

fill성명서는 올바르게 켜고 끄고 있음을 확인하기위한 것입니다. 그것은.

사용하는 sort것이 옵션이라고 들었지만 "선택된"요소를 맨 위로 가져 오기 위해 어떻게 구현되는지 확실하지 않습니다.

최신 정보:

mouseover이벤트 의 SVG에 하이라이트를 표시 하기 위해 새 호를 추가하는 다음 코드로 특정 상황을 수정했습니다 .

svg.selectAll("path")
    .data(d)
  .enter().append("path")
    .attr("d", arc)
    .attr("class", "arc")
    .style("fill", function(d) { return color(d.name); })
    .style("stroke", "#fff")
    .style("stroke-width", 0)
    .on("mouseover", function(d) {
        svg.append("path")
          .attr("d", d3.select(this).attr("d"))
          .attr("id", "arcSelection")
          .style("fill", "none")
          .style("stroke", "#fff")
          .style("stroke-width", 2);
    })
    .on("mouseout", function(d) {
        d3.select("#arcSelection").remove();
    });

개발자가 제시 한 솔루션 중 하나는 "D3의 정렬 연산자를 사용하여 요소를 재정렬"하는 것입니다. ( https://github.com/mbostock/d3/issues/252 참조 )

이 관점에서 데이터를 비교하여 요소를 정렬하거나 데이터가없는 요소 인 경우 위치를 정렬 할 수 있습니다.

.on("mouseover", function(d) {
    svg.selectAll("path").sort(function (a, b) { // select the parent and sort the path's
      if (a.id != d.id) return -1;               // a is not the hovered element, send "a" to the back
      else return 1;                             // a is the hovered element, bring "a" to the front
  });
})

다른 답변에서 설명한 것처럼 SVG에는 Z- 색인 개념이 없습니다. 대신 문서의 요소 순서에 따라 도면의 순서가 결정됩니다.

요소를 수동으로 재정렬하는 것 외에도 특정 상황에 대한 다른 방법이 있습니다.

D3로 작업하면 항상 다른 유형의 요소 위에 그려야하는 특정 유형의 요소가 있습니다 .

예를 들어 그래프를 배치 할 때 링크는 항상 노드 아래에 배치되어야합니다. 일반적으로 일부 배경 요소는 일반적으로 다른 모든 요소 아래에 배치해야하며 일부 하이라이트 및 오버레이는 위에 배치해야합니다.

이러한 상황이 발생하면 해당 요소 그룹에 대한 상위 그룹 요소만드는 것이 가장 좋은 방법 이라는 것을 알았습니다 . SVG에서는이를 위해 g요소를 사용할 수 있습니다 . 예를 들어, 항상 노드 아래에 위치해야하는 링크가있는 경우 다음을 수행하십시오.

svg.append("g").attr("id", "links")
svg.append("g").attr("id", "nodes")

이제 링크와 노드를 그릴 때 다음과 같이 선택하십시오 ( #요소 ID 참조로 시작하는 선택기 ).

svg.select("#links").selectAll(".link")
// add data, attach elements and so on

svg.select("#nodes").selectAll(".node")
// add data, attach elements and so on

이제 모든 링크는 항상 모든 노드 요소 앞에 구조적으로 추가됩니다. 따라서 SVG는 요소를 추가하거나 제거하는 빈도와 순서에 관계없이 모든 노드 아래의 모든 링크를 표시합니다. 물론 동일한 유형 (즉, 동일한 컨테이너 내)의 모든 요소는 추가 된 순서의 적용을받습니다.


SVG에는 Z- 색인이 없지만 DOM 요소의 순서를 사용하므로 다음과 같이 앞쪽으로 가져올 수 있습니다.

this.parentNode.appendChild(this);

그런 다음 예를 들어 insertBefore사용 하여 다시 넣을 수 있습니다 mouseout. 그러나이를 위해서는 요소가 이전에 삽입되어야하는 형제 노드를 대상으로 할 수 있어야합니다.

데모 : JSFiddle 살펴보기


SVG는 Z- 색인을 수행하지 않습니다. Z 순서는 컨테이너에있는 SVG DOM 요소의 순서에 따라 결정됩니다.

내가 말할 수있는 한 (그리고 과거에 이것을 몇 번 시도 했음) D3는 단일 요소를 맨 앞으로 가져 오기 위해 분리하고 다시 연결하는 방법을 제공하지 않습니다.

선택 항목에 나타나는 순서와 일치하도록 노드를 다시 섞는 .order()방법 이 있습니다 . 귀하의 경우에는 단일 요소를 맨 앞으로 가져와야합니다. 따라서 기술적으로 원하는 요소를 앞에두고 (또는 마지막에 맨 위에있는 요소를 기억할 수 없음) 선택한 다음 호출 order()할 수 있습니다.

또는이 작업에서 d3를 건너 뛰고 일반 JS (또는 jQuery)를 사용하여 단일 DOM 요소를 다시 삽입 할 수 있습니다.


간단한 대답은 d3 주문 방법을 사용하는 것입니다. d3.select ( 'g'). order () 외에도 버전 4에는 .lower () 및 .raise ()가 있습니다. 이는 요소가 나타나는 방식을 변경합니다. 자세한 정보는 문서를 참조하십시오-https: //github.com/d3/d3/blob/master/API.md#selections-d3-selection


나는 내 코드에 futurend의 솔루션을 구현했고 작동했지만 내가 사용하고 있던 많은 수의 요소로 인해 매우 느렸다. 내 특정 시각화를 위해 더 빠르게 작동하는 jQuery를 사용하는 대체 방법이 있습니다. 공통된 클래스를 갖는 위에 원하는 svg에 의존합니다 (제 예에서 클래스는 내 데이터 세트에서 d.key로 표시됩니다). 내 코드에는 <g>내가 재구성하는 모든 SVG를 포함하는 "위치"클래스가 있습니다.

.on("mouseover", function(d) {
    var pts = $("." + d.key).detach();
    $(".locations").append(pts);
 });

따라서 특정 데이터 포인트 위로 마우스를 가져 가면 코드는 해당 특정 클래스가있는 SVG DOM 요소가있는 다른 모든 데이터 포인트를 찾습니다. 그런 다음 해당 데이터 포인트와 관련된 SVG DOM 요소를 분리하고 다시 삽입합니다.


완전히 새로운 답변을 작성하는 대신 @ notan3xit가 답변 한 내용을 확장하고 싶었습니다 (하지만 평판이 충분하지 않습니다).

요소 순서 문제를 해결하는 또 다른 방법은 그릴 때 '추가'가 아닌 '삽입'을 사용하는 것입니다. 이렇게하면 경로가 항상 다른 svg 요소 앞에 함께 배치됩니다 (이는 코드가 이미 다른 svg 요소에 대해 enter () 전에 링크에 대해 enter ()를 수행한다고 가정합니다).

d3 삽입 API : https://github.com/mbostock/d3/wiki/Selections#insert


It took me ages to find how to tweak the Z-order in an existing SVG. I really needed it in the context of d3.brush with tooltip behavior. In order to have the two features work nicely together (http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html), you need the d3.brush to be the first in Z-order (1st to be drawn on the canvas, then covered by the rest of the SVG elements) and it will capture all mouse events, no matter what is on top of it (with higher Z indices).

Most forum comments say that you should add the d3.brush first in your code, then your SVG "drawing" code. But for me it was not possible as I loaded an external SVG file. You can easily add the brush at any time and alter the Z-order later on with:

d3.select("svg").insert("g", ":first-child");

In the context of a d3.brush setup it will look like:

brush = d3.svg.brush()
    .x(d3.scale.identity().domain([1, width-1]))
    .y(d3.scale.identity().domain([1, height-1]))
    .clamp([true,true])
    .on("brush", function() {
      var extent = d3.event.target.extent();
      ...
    });
d3.select("svg").insert("g", ":first-child");
  .attr("class", "brush")
  .call(brush);

d3.js insert() function API: https://github.com/mbostock/d3/wiki/Selections#insert

Hope this helps!


Version 1

In theory, the following should work fine.

The CSS code :

path:hover {
    stroke: #fff;
    stroke-width : 2;
}

This CSS code will add a stroke to the selected path.

The JS code :

svg.selectAll("path").on("mouseover", function(d) {
    this.parentNode.appendChild(this);
});

This JS code first removes the path from the DOM tree and then adds it as the last child of its parent. This makes sure the path is drawn on top of all other children of the same parent.

In practice, this code works fine in Chrome but breaks in some other browsers. I tried it in Firefox 20 on my Linux Mint machine and couldn't get it to work. Somehow, Firefox fails to trigger the :hover styles and I haven't found a way to fix this.


Version 2

So I came up with an alternative. It may be a bit 'dirty', but at least it works and it doesn't require looping over all elements (as some of the other answers).

The CSS code :

path.hover {
    stroke: #fff;
    stroke-width : 2;
}

Instead of using the :hover pseudoselector, I use a .hover class

The JS code :

svg.selectAll(".path")
   .on("mouseover", function(d) {
       d3.select(this).classed('hover', true);
       this.parentNode.appendChild(this);
   })
   .on("mouseout", function(d) {
       d3.select(this).classed('hover', false);
   })

On mouseover, I add the .hover class to my path. On mouseout, I remove it. As in the first case, the code also removes the path from the DOM tree and then adds it as the last child of its parent.


You can Do like this On Mouse Over You can Pull it to top.

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

d3.selection.prototype.pushElementAsBackLayer = function() { 
return this.each(function() { 
    var firstChild = this.parentNode.firstChild; 
    if (firstChild) { 
        this.parentNode.insertBefore(this, firstChild); 
    } 
}); 

};

nodes.on("mouseover",function(){
  d3.select(this).bringElementAsTopLayer();
});

If You want To Push To Back

nodes.on("mouseout",function(){
   d3.select(this).pushElementAsBackLayer();
});

참고URL : https://stackoverflow.com/questions/13595175/updating-svg-element-z-index-with-d3

반응형