Обновление SVG элемента Z-Index с помощью D3



каков эффективный способ довести элемент SVG до вершины z-порядка, используя библиотеку D3?



мой конкретный сценарий-это круговая диаграмма, которая выделяет (путем добавления 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 оба не работают.



класс" top " определяется следующим образом в моем CSS:



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


The fill заявление есть, чтобы убедиться, что я знал, что он включается/выключается правильно. Это.



Я слышал, что с помощью sort это вариант, но мне непонятно, как он будет реализован для приведения "выбранного" элемента к вершине.



обновление:



я исправил свою конкретную ситуацию с помощью следующего кода, который добавляет новую дугу в SVG на mouseover событие, чтобы показать выделить.



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

10 ответов:

одно из решений, представленных разработчиком: "используйте оператор сортировки 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")

теперь, когда вы рисуете свои ссылки и узлы, выберите следующим образом (селекторы, начинающиеся с # ссылка на идентификатор элемента):

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. Однако для этого требуется, чтобы вы могли нацелиться на узел-брат, который ваш элемент должен быть вставлен раньше.

DEMO: взгляните на это JSFiddle

SVG не делает Z-индекс. Z-порядок диктуется порядком элементов SVG DOM в их контейнере.

есть .order() метод, что перестановки узлов в соответствии с порядком, они появляются в выборе. В вашем случае, вам нужно принести одиночный элемент к фронту. Таким образом, технически вы можете прибегнуть к выбору с нужным элементом спереди (или в конце, не можете вспомнить, какой из них самый верхний), а затем вызвать order() на нем.

или вы можете пропустить d3 для этой задачи и использовать обычный JS (или jQuery), чтобы повторно вставить этот единственный элемент DOM.

я реализовал решение futurend в своем коде, и это сработало, но с большим количеством элементов, которые я использовал, это было очень медленно. Вот альтернативный метод с использованием jQuery, который работал быстрее для моей конкретной визуализации. Он полагается на svgs, которые вы хотите сверху, имеющие общий класс (в моем примере класс отмечен в моем наборе данных как D.key). В моем коде есть <g> С классом "locations", который содержит все SVG, которые я реорганизую.

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

Так когда при наведении указателя мыши на определенную точку данных код находит все остальные точки данных с элементами SVG DOM с этим конкретным классом. Затем он отсоединяет и повторно вставляет элементы SVG DOM, связанные с этими точками данных.

хотел бы расширить то, что @notan3xit ответил, а не выписать совершенно новый ответ (но у меня недостаточно репутации).

другой способ решить проблему порядка элементов-использовать "вставить", а не "добавить" при рисовании . Таким образом, пути всегда будут помещены вместе перед другими элементами svg(это предполагает, что ваш код уже делает enter() для ссылок перед enter() для других элементов svg).

D3 вставить api: https://github.com/mbostock/d3/wiki/Selections#insert

простой ответ заключается в использовании методов упорядочения d3. В дополнение к Д3.выберите('г').порядок (), есть .низкий и. raise () в версии 4. Это изменяет способ отображения элементов. Пожалуйста, обратитесь к документам для получения дополнительной информации -https://github.com/d3/d3/blob/master/API.md#selections-d3-selection

мне потребовались годы, чтобы найти, как настроить Z-порядок в существующем SVG. Мне это действительно было нужно в контексте d3.кисть с подсказкой поведения. Для того, чтобы эти две функции хорошо работали вместе (http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html), Вам нужен d3.кисть должна быть первой в Z-порядке (1-я должна быть нарисована на холсте, а затем покрыта остальными элементами SVG), и она будет захватывать все события мыши, независимо от того, что находится поверх нее (с более высоким Z индексы.)

большинство комментариев на форуме говорят, что вы должны добавить d3.кисть сначала в вашем коде, а затем ваш SVG "рисунок" код. Но для меня это было невозможно, так как я загрузил внешний SVG-файл. Вы можете легко добавить кисть в любое время и изменить Z-порядок позже с помощью:

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

в контексте d3.настройка кисти будет выглядеть так:

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

надеюсь, что это помогает!

Вариант 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, но ломается в некоторых других браузерах. Я попробовал его в Firefox 20 на моей машине Linux Mint и не смог заставить его работать. Каким-то образом Firefox не может вызвать :hover стили и я не нашел способ исправить это.


Вариант 2

поэтому я придумал альтернативу. Он может быть немного "грязным", но, по крайней мере, он работает и не требует циклического перебора всех элементов (как и некоторые другие ответы).

код CSS :

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

вместо :hover pseudoselector, я использую .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, а затем добавляет его в качестве последнего дочернего элемента своего родителя.

вы можете сделать это на мыши над вы можете вытащить его на вершину.

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

Comments

    Ничего не найдено.