Как эффективно вывести 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
обоих не помогли.
В моем CSS класс "top" определен следующим образом:
.top {
fill: red;
z-index: 100;
}
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();
});
источник
a.id
иd.id
. Решение состоит в том, чтобы напрямую сравнитьa
иd
.<g>
.svg:use
элемент может динамически указывать на поднятый элемент. См. Решение stackoverflow.com/a/6289809/292085 .Как объясняется в других ответах, 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
. Однако это требует, чтобы вы могли нацеливаться на то, что ваш элемент должен быть вставлен раньше.ДЕМО: взгляните на этот JSFiddle
источник
:hover
стили. Я также не думаю, что в этом случае потребуется функция вставки в конце.insert
функция d3, вероятно, не лучший вариант, потому что она создает новый элемент. Мы просто хотим переместить существующий элемент, поэтомуinsertBefore
вместо этого я использую в этом примере.SVG не выполняет z-index. Z-порядок определяется порядком элементов SVG DOM в их контейнере.
Насколько я мог судить (и я пробовал это пару раз в прошлом), D3 не предоставляет методов для отсоединения и повторного присоединения одного элемента, чтобы вывести его на передний план или еще много чего.
Существует
.order()
метод , который перетасовывает узлы в соответствии с порядком их появления в выделении. В вашем случае вам нужно вывести на передний план единственный элемент. Таким образом, технически вы можете прибегнуть к выделению, указав желаемый элемент впереди (или в конце, не помня, какой из них самый верхний), а затем вызватьorder()
его.Или вы можете пропустить d3 для этой задачи и использовать простой JS (или jQuery), чтобы повторно вставить этот единственный элемент DOM.
источник
.style('pointer-events', 'none')
наложенный путь. К сожалению, это свойство ничего не делает в IE, но все же ....Простой ответ - использовать методы упорядочивания d3. В дополнение к d3.select ('g'). Order () в версии 4 есть .lower () и .raise () . Это меняет внешний вид ваших элементов. Для получения дополнительной информации обратитесь к документации - https://github.com/d3/d3/blob/master/API.md#selections-d3-selection
источник
Я реализовал решение futurend в своем коде, и оно сработало, но с большим количеством элементов, которые я использовал, было очень медленно. Вот альтернативный метод с использованием jQuery, который работал быстрее для моей конкретной визуализации. Он полагается на svgs, которые вы хотите иметь общий класс (в моем примере класс отмечен в моем наборе данных как d.key). В моем коде есть
<g>
класс "location", содержащий все реорганизуемые мной SVG-файлы..on("mouseover", function(d) { var pts = $("." + d.key).detach(); $(".locations").append(pts); });
Поэтому, когда вы наводите курсор на определенную точку данных, код находит все остальные точки данных с элементами SVG DOM с этим конкретным классом. Затем он отсоединяет и повторно вставляет элементы SVG DOM, связанные с этими точками данных.
источник
Хотел подробнее рассказать о том, что ответил @ notan3xit, вместо того, чтобы писать новый ответ (но у меня недостаточно репутации).
Другой способ решить проблему порядка элементов - использовать при рисовании «вставить», а не «добавить». Таким образом, пути всегда будут располагаться вместе перед другими элементами svg (предполагается, что ваш код уже выполняет ввод () для ссылок перед вводом () для других элементов svg).
d3 вставить api: https://github.com/mbostock/d3/wiki/Selections#insert
источник
Мне потребовалось время, чтобы найти, как настроить Z-порядок в существующем SVG. Мне это действительно было нужно в контексте d3.brush с поведением всплывающей подсказки. Чтобы эти две функции хорошо работали вместе ( http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html ), вам нужно, чтобы d3.brush был первым в Z-порядке (1-й должен быть нарисован на холсте, затем покрыт остальными элементами 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);
API функции d3.js insert (): 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
псевдоселектора я использую.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
класс на свой путь. При наведении курсора удаляю. Как и в первом случае, код также удаляет путь из дерева 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(); });
источник