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


81

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

답변:


52

개발자가 제시 한 솔루션 중 하나는 "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
  });
})

이것은 Z 순서에서만 작동 할뿐만 아니라 요소가 올라간 상태에서 방금 시작한 드래그를 화나게하지 않습니다.
올리버 복

3
그것은 나에게도 작동하지 않습니다. a, b는 d3 선택 요소 나 DOM 요소가 아닌 데이터 일 뿐인 것 같습니다
nkint

실제로 @nkint와 같이 비교할 때 be a.idd.id. 이 솔루션은 직접 비교하는 것입니다 ad.
평화는 풍요

이것은 많은 수의 자식 요소에 대해 적절하게 수행되지 않습니다. 후속 .NET에서 제기 된 요소를 다시 렌더링하는 것이 <g>좋습니다.
tribalvibes

1
또는 svg:use요소가 제기 된 요소를 동적으로 가리킬 수 있습니다. 해결 방법 은 stackoverflow.com/a/6289809/292085 를 참조하십시오 .
tribalvibes

91

다른 답변에서 설명한 것처럼 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는 요소를 추가하거나 제거하는 빈도와 순서에 관계없이 모든 노드 아래의 모든 링크를 표시합니다. 물론 동일한 유형 (즉, 동일한 컨테이너 내)의 모든 요소는 추가 된 순서를 따릅니다.


1
내 진짜 문제는 내 요소를 그룹으로 구성하는 것이었을 때 하나의 항목을 위로 이동할 수 있다는 생각으로이 문제에 부딪 혔습니다. "실제"문제가 개별 요소를 이동하는 것이라면 이전 답변에서 설명한 정렬 방법이 좋은 방법입니다.
John David Five

대박! 감사합니다, notan3xit, 당신은 내 하루를 구했습니다! 다른 사람을위한 아이디어를 추가하기 위해-원하는 가시성 순서와 다른 순서로 요소를 추가해야하는 경우 적절한 가시성 순서로 그룹을 생성 한 다음 (요소를 추가하기 전에도) 여기에 요소를 추가 할 수 있습니다. 순서에 관계없이 그룹.
Olga Gnatenko 2014

1
이것이 올바른 접근 방식입니다. 당신이 그것을 생각하면 분명합니다.
Dave

저에게는 svg.select ( '# links') ...가 작동하려면 d3.select ( '# links') ...로 대체되어야했습니다. 아마도 이것은 다른 사람들을 도울 것입니다.
교환

26

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

this.parentNode.appendChild(this);

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

데모 : JSFiddle 살펴보기


Firefox가 :hover스타일 을 렌더링하는 데 실패하지 않았다면 이것은 훌륭한 솔루션이 될 것 입니다. 또한 마지막에 "nsert 함수"가이 사용 사례에 필요하다고 생각하지 않습니다.
John Slegers

@swenedo, 귀하의 솔루션은 저에게 잘 작동하며 요소를 전면에 가져옵니다. 그것을 뒤로 가져 오는 것에 관해서는 약간 쌓여 있고 올바른 방식으로 쓰는 법을 모릅니다. 물체를 뒤로 가져 오는 방법에 대한 예를 게시 해 주시겠습니까? 감사.
Mike B.

1
@MikeB. 물론입니다. 방금 답변을 업데이트했습니다. 나는 d3의 insert기능이 새로운 요소를 생성하기 때문에 아마도 최선의 방법이 아니라는 것을 깨달았습니다 . 기존 요소를 이동하고 싶기 insertBefore때문에이 예제에서 대신 사용합니다.
swenedo

이 방법을 사용하려고했지만 안타깝게도 IE11에서는 작동하지 않습니다. 이 브라우저에 대한 해결 방법이 있습니까?
Antonio Giovanni Schiavone 2016 년

13

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

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

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

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


5
일부 브라우저에서 마우스 오버 핸들러 내부에 요소를 제거 / 삽입하는 데 문제가 있으며, 마우스 아웃 이벤트가 제대로 전송되지 않을 수 있으므로 모든 브라우저에서이 작업을 시도하여 예상대로 작동하는지 확인하는 것이 좋습니다.
Erik Dahlström

순서를 변경해도 원형 차트의 레이아웃이 변경되지 않습니까? 또한 요소를 다시 삽입하는 방법을 배우기 위해 jQuery를 살펴보고 있습니다.
Evil Closet Monkey

내가 선택한 솔루션으로 내 질문을 업데이트했는데 실제로 원래 질문의 핵심을 활용하지 않습니다. 이 솔루션에 대해 정말 나쁜 점을 발견하면 의견을 환영합니다! 원래 질문에 대한 통찰력에 감사드립니다.
Evil Closet Monkey

주로 겹쳐진 모양에 채우기가 없기 때문에 합리적인 접근 방식으로 보입니다. 만약 그렇다면, 마우스 이벤트가 나타나는 순간 "훔칠"것으로 예상합니다. 그리고 저는 @ ErikDahlström의 주장이 여전히 유효하다고 생각합니다. 이것은 일부 브라우저 (주로 IE9)에서 여전히 문제가 될 수 있습니다. 추가 "안전" .style('pointer-events', 'none')을 위해 중첩 된 경로에 추가 할 수 있습니다 . 불행히도,이 속성은 IE에서 아무것도하지 않지만 여전히 ....
meetamit

@EvilClosetMonkey 안녕하세요 ...이 질문은 많은 의견을 얻었으며 아래 futurend의 답변이 저보다 더 도움이된다고 생각합니다. 따라서 수락 된 답변을 futurend로 변경하는 것을 고려해보십시오.
meetamit 2013 년


4

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

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

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


4

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

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

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


1

기존 SVG에서 Z 순서를 조정하는 방법을 찾는 데 오랜 시간이 걸렸습니다. 툴팁 동작이있는 d3.brush 컨텍스트에서 정말 필요했습니다. 두 기능이 함께 잘 작동하도록하려면 ( http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html ), d3.brush가 Z 순서에서 첫 번째가되어야합니다. (첫 번째는 캔버스에 그린 다음 나머지 SVG 요소로 덮음) 위에있는 항목에 관계없이 모든 마우스 이벤트를 캡처합니다 (더 높은 Z 인덱스 사용).

대부분의 포럼 댓글은 먼저 코드에 d3.brush를 추가 한 다음 SVG "드로잉"코드를 추가해야한다고 말합니다. 하지만 저에게는 외부 SVG 파일을로드했기 때문에 불가능했습니다. 언제든지 브러시를 쉽게 추가하고 나중에 다음을 사용하여 Z 순서를 변경할 수 있습니다.

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

d3.brush 설정의 맥락에서 다음과 같습니다.

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 () 함수 API : https://github.com/mbostock/d3/wiki/Selections#insert

도움이 되었기를 바랍니다!


0

버전 1

이론적으로 다음은 잘 작동합니다.

CSS 코드 :

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

이 CSS 코드는 선택한 경로에 획을 추가합니다.

JS 코드 :

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

이 JS 코드는 먼저 DOM 트리에서 경로를 제거한 다음 부모의 마지막 자식으로 추가합니다. 이렇게하면 경로가 동일한 부모의 다른 모든 자식 위에 그려집니다.

실제로이 코드는 Chrome에서는 잘 작동하지만 다른 브라우저에서는 작동하지 않습니다. Linux Mint 컴퓨터의 Firefox 20에서 시도했지만 작동하지 못했습니다. 어쨌든 Firefox는 :hover스타일 을 트리거하지 못하며이 문제를 해결할 방법을 찾지 못했습니다.


버전 2

그래서 대안을 생각해 냈습니다. 약간 '더러울'수 있지만 적어도 작동하며 모든 요소를 ​​반복 할 필요가 없습니다 (다른 답변 중 일부).

CSS 코드 :

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

:hover의사 선택기 를 사용하는 대신 .hover클래스를 사용합니다.

JS 코드 :

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

마우스를 올리면 .hover내 경로에 클래스를 추가합니다 . mouseout에서 제거합니다. 첫 번째 경우와 마찬가지로 코드는 DOM 트리에서 경로를 제거한 다음 부모의 마지막 자식으로 추가합니다.


0

이렇게 할 수 있습니다. 마우스를 올려 놓으면 맨 위로 당길 수 있습니다.

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

뒤로 밀고 싶다면

nodes.on("mouseout",function(){
   d3.select(this).pushElementAsBackLayer();
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.