Для начала я собираюсь упомянуть, почему так важно сделать правильный выбор.Например, здесь:
group.append("path")
...
.on("mouseover", fade(5))
.on("mouseout", fade(5));
Вы применяете прослушиватели для событий мыши, но ниже вас (и вы, возможно, пытаетесь захватить ленты, но я не уверен), вы перезаписываете оба этихСлушатели с:
svg.selectAll("path")
.on("mouseover", function(d) { ...
И с помощью более сложного метода (который не следует обычному шаблону) вы также перезаписываете слушателя на mouseout
.Прослушиватель событий может быть назначен только один раз для определенного элемента.И поскольку нам нужны разные слушатели как для групп (внешних дуг), так и для лент, нам необходимо изменить этот подход.
Хорошая новость заключается в том, что это может привести к гораздо более короткому и четкому коду.Чтобы повторно использовать ваш выбор ленты, я создал переменную для хранения ее с именем ribbons
, переменная group
продолжит хранить внешние дуги.
Я собираюсь разбить три основных событияЯ вижу, что вы пытаетесь сделать:
- Ничего не выделяя
Это должно быть довольно просто, мы можем использовать такую функцию, как:
function showAllRibbons() {
ribbons.style("opacity",1)
}
Подсветка одиночного пути на событии мыши:
Это также довольно просто, мы скрываем все остальное и показываем элемент, на который this
указывает:
function highlightOneRibbon() {
ribbons.style("opacity",0.1);
d3.select(this).style("opacity",1);
}
Выделите все ленты, которые имеют источник или цель в определенной внешней дуге.
Это самая сложная задача, она помогает увидеть структуру данных каждой ленты, которая имеет свойства для идентификатора двух якорей, расположенных по адресу: d.source.index
и d.target.index
.Теперь, используя индекс каждой внешней дуги в группе выбора, мы можем сделать простой фильтр для тех лент, которые соответствуют базовым критериям:
function highlightRibbons(d,i) {
ribbons.style("opacity",0.1); // set all relatively transparent
// fix the ones that need to be shown:
ribbons.filter(function(r) {
if(r.target.index == i || r.source.index == i) return r;
})
.style("opacity",1);
Конечно, вы можете получить немного более изящные, фильтровать те, которые нене соответствует критериям для их постепенного исчезновения, отфильтруйте те, которые используются для постепенного появления.
Эта функция является еще одним доказательством того, что вам нужны разные прослушиватели событий для ленты и дуги, так как эта функция не будетработать с d3.selectAll("path").on("mouseouver,highlightRibbons)
, потому что наши индексы могут быть неправильными (так как мы выбираем ленты тоже, но эта функция также не имеет смысла для лент).
Хорошо, так.Давайте удалим часть вашего кода, который использует d3.selectAll («путь») для манипулирования уже существующими элементами (около строки 247?), Так как это вызывает у нас некоторое горе, пока мы находимся, как слушатели событий, которые вызываютфункция fade
перезаписана, мы ее не используем, давайте также отбросим эту функцию.
В результате я теряю интерактивность всплывающей подсказки, но у этого, похоже, были некоторые проблемы, не зависящие от вопроса (но, опять же, отдельные слушатели, вероятно, требуются для группы и ленты, так что подсказка имеет соответствующую информациюоснованный на типе функции, на которую он ссылается).
Вот небольшая демонстрация результата с небольшим добавлением таланта для отображения группы (переходы) (потому что фрагменты не любят больше кода, кажется, яобрезаны стили подсказок, кнопки и т. д., так как они в любом случае не работают или не используются в этой демонстрации):
var names = ["Ukraine","Spain","Slovenia","Lithuania","Austria","Estonia","Norway","Portugal","UK","Serbia","Germany","Albania","France","Czech Republic","Denmark","Australia","Finland","Bulgaria","Moldova","Sweden","Hungary","Israel","Netherlands","Ireland","Cyprus","Italy"];
var opacityDefault = 0.7;
var matrix = [
[0.1,0,0,0,0,0,0,4,0,0,0,0,4,12,0,0,0,1,15,0,0,7,0,0,2,8], //Ukraine
[0,0,0,0,0,0,2,14,1,0,6,0,5,0,6,7,0,0,0,0,0,0,0,1,7,0], //Spain
[5,0,0,0,0,0,0,3,0,8,0,0,2,7,0,0,0,0,0,0,0,4,1,0,0,0], //Slovenia
[2,0,0,0,0,22,15,6,12,0,8,0,0,1,0,3,0,10,0,7,5,0,7,12,6,0], //Lithuania
[7,8,12,15,0,18,16,8,12,4,15,2,7,5,18,5,10,16,3,12,11,13,13,5,2,7], //Austria
[4,3,5,12,2,0,0,19,6,3,2,4,7,5,7,8,12,2,13,0,0,8,4,7,5,10], //Estonia
[0,3,5,0,0,6,0,0,5,9,0,0,0,0,8,1,0,0,7,10,7,0,4,0,5,12], //Norway
[0,0,0,7,0,3,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,2,6,0,0], //Portugal
[0,0,0,0,0,0,0,0,0,0,1,3,3,0,3,6,0,0,0,0,0,8,0,10,0,6], //UK
[0,0,0,0,8,0,0,0,0,0,0,3,1,0,0,0,0,7,0,0,0,0,0,0,0,1], //Serbia
[0,13,4,7,16,6,18,8,3,10,0,14,8,3,24,12,1,6,8,6,1,8,24,16,3,13], //Germany
[0,0,5,0,9,0,0,10,7,1,0,0,0,6,0,0,0,7,0,0,10,0,0,2,6,12], //Albania
[19,10,2,15,0,0,8,5,0,0,0,12,0,0,2,0,9,5,0,5,0,6,0,4,4,0], //France
[14,14,11,8,15,5,4,0,5,5,8,1,4,0,6,4,5,11,6,3,8,12,6,7,13,0], //Czech Republic
[11,0,7,10,5,8,10,2,2,4,3,0,2,7,0,10,13,0,0,12,24,0,8,2,0,12], //Denmark
[2,0,0,0,0,0,6,0,1,0,7,0,10,0,12,0,0,0,7,8,7,7,0,0,0,0], //Australia
[0,0,0,0,0,12,0,0,4,0,0,0,0,3,0,4,0,0,0,9,0,6,0,0,0,0], //Finland
[0,5,0,0,6,7,0,7,14,2,0,14,0,5,1,0,10,0,11,6,0,1,0,10,12,0], //Bulgaria
[6,0,6,0,0,1,0,6,4,6,0,2,6,6,0,7,0,8,0,0,2,10,0,1,10,10], //Moldova
[6,2,12,12,8,5,13,0,2,12,12,4,5,8,11,12,8,2,0,0,1,10,8,0,12,1], //Sweden
[1,0,3,2,3,3,0,0,0,12,2,0,0,2,0,0,8,5,2,0,0,3,2,0,4,0], //Hungary
[22,22,1,7,19,0,7,2,17,9,11,6,24,22,3,18,19,14,22,17,16,0,15,13,10,9], //Israel
[8,0,7,3,1,1,9,0,0,7,5,0,6,0,5,0,2,0,0,1,8,0,0,3,0,0], //Netherlands
[0,6,0,4,8,0,1,3,13,0,15,7,1,14,4,12,2,1,0,4,3,0,4,0,0,5], //Ireland
[4,20,14,7,1,12,7,5,8,10,9,20,3,8,6,7,7,15,13,16,7,2,11,17,0,8], //Cyprus
[5,10,10,7,10,7,0,14,0,14,12,24,10,2,0,0,10,6,8,0,6,5,7,0,15,0], //Italy
];
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
outerRadius = Math.min(width, height) * 0.4 - 100,
innerRadius = outerRadius - 20;
var formatValue = d3.formatPrefix(",.0", 1e3);
var chord = d3.chord()
.padAngle(0.05)
.sortSubgroups(d3.descending);
var arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var ribbon = d3.ribbon()
.radius(innerRadius);
var color = d3.scaleOrdinal()
.domain(d3.range(4))
.range(["#4B5320", "#50C878", "#98FB98", "#679267","#2E8B57","#043927", "#0B6623","#9DC183","#708238", "#C7EA46", "#3F704D","#00A86B","8F9779"]);
var g = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
.datum(chord(matrix));
// Append the groups:
var group = g.append("g")
.attr("class", "groups")
.selectAll("g")
.data(function(chords) { return chords.groups; })
.enter().append("g");
group.append("path")
.style("fill", function(d) { return color(d.index); })
.style("stroke", function(d) { return d3.rgb(color(d.index)).darker(); })
.attr("d", arc)
.on("mouseover", highlightRibbons)
.on("mouseout", showAllRibbons)
group.append("text")
.each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("dy", ".35em")
.attr("class", "titles")
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.attr("transform", function(d) {
if (outerArcs = 0) {
width = 10;
}
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + (outerRadius + 10) + ")"
+ (d.angle > Math.PI ? "rotate(180)" : "");
})
.text(function(d,i) { return names[i]; });
// Append the ribbons
var ribbons = g.append("g") // save the selection as a variable
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", ribbon)
.style("fill", function(d) { return color(d.target.index); })
.attr("class","ribbon")
.style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); })
.on("mouseover",highlightOneRibbon)
.on("mouseout",showAllRibbons);
// New Functions:
// Highlight
function highlightRibbons(d,i) {
ribbons.filter(function(r) {
if(!(r.target.index == i || r.source.index == i)) return r;
})
.transition()
.style("opacity",0.1)
.duration(500);
ribbons.filter(function(r) {
if(r.target.index == i || r.source.index == i) return r;
})
.transition()
.style("opacity",1)
.duration(500);
}
// Unhighlight
function showAllRibbons() {
ribbons
//.transition() // don't use for individual ribbons - transition is too long
.style("opacity",1)
//.duration(500); // any visually effective transition will be too long given how quickly the mouse can cross multiple paths
}
// show a particular ribbon:
function highlightOneRibbon() {
ribbons.style("opacity",0.1);
d3.select(this).style("opacity",1);
}
// Returns an array of tick angles and values for a given group and step.
function groupTicks(d, i) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(0, d.value,0.2).map(function(v, i) {
return {
angle: v * k + d.startAngle,
label: i*100 % 5 ? null : v,
id: i
};
});
}
.group-tick line {
stroke: #000;
}
.ribbons {
fill-opacity: 1;
}
<svg width="1000" height="1000"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>