D3 Mouse Over Groups问题

时间:2018-05-29 10:22:42

标签: javascript d3.js mouseover

我正在尝试为显示相应和弦的每组国家/地区执行鼠标悬停。我已经在我的代码底部创建了一个不透明度函数,但由于某种原因它似乎没有注册组或鼠标悬停功能。

            <!DOCTYPE html>
            <meta charset="utf-8">
            <head>
            <style>
            body {
              font: 10px sans-serif;
              background-color: #F0F0F0;

            }

            h1 {
              font-size: 400%;
              position: relative;
              font-family: Helvetica;
            }

            h3 {
              font-family: Helvetica;
              position: relative;
            }

            p {
              position: relative;
              font-style: italic;
              font-family: Helvetica;
              top:1000px;
              left: 100px;
            }


            @media only screen and (max-width:1000px) {
              /* For tablets: */
              .main {
                width: 80%;
                padding: 0;
              }
              .right {
                width: 100%;
              }
            }

            button {
                background-color: #4CA350;
                position: relative;
                border: none;
                color: white;
                padding: 15px 32px;
                text-align: center;
                text-decoration: none;
                display: inline-block;
                font-size: 16px;
                margin: 4px 2px;
                cursor: pointer;
            }

            .group-tick line {
              stroke: #000;
            }

            .ribbons {
              fill-opacity: 1;

            }

            #tooltip.total {
                position: relative;
                width: 200px;
                height: auto;
                padding: 10px;
                background-color: white;
                -webkit-border-radius: 10px;
                -moz-border-radius: 10px;
                border-radius: 10px;
                -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                pointer-events: none;
            }

            #tooltip {
                position: relative;
                font-family: Helvetica Neue;
                font-size: 16px;
                top:870px;
                left:720px;
                width: 200px;
                height: auto;
                padding: 10px;
                background-color: white;
                -webkit-border-radius: 10px;
                -moz-border-radius: 10px;
                border-radius: 10px;
                -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
                pointer-events: none;
            }

            #tooltip.hidden {
                display: none;
            }

            #tooltip p {
                margin: 0;
                font-family: sans-serif;
                font-size: 40px;
                line-height: 20px;
            }
            </style>
            <title>Julia Spyrou DECO3100 Assignment 3</title>
            <body>
            <h1>Voting at the Eurovision Song Contest</h1>
            <h3>Mouseover individual chords to view voting relationships</h3>
              <p>NB: These visualisations exclude the countries that did not make the Final and their votes as well</p>
              <button type="button">2016</button>
              <button type="button">2017</button>
              <button type="button">2018</button>
              <div id="tooltip" class="hidden">
                <p>$<span id="value">100</span></p>
              </div>

            <svg width="1000" height="1000"></svg>
            <script src="https://d3js.org/d3.v4.min.js"></script>
            <script>



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

            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", fade(5))
                .on("mouseout", fade(5));

            function fade(opacity) {
              return function(d, i) {
              svg.selectAll(".ribbons path")
                  .filter(function(d) { return d.source.index != i && d.target.index != i; })
                  .transition()
                 };
            }

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

            g.append("g")
                .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); })
                .style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); });

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

            svg.selectAll("path")
                .on("mouseover", function(d) { //event when we mouseover
                  d3.select(this) //this gives us acess to the particular rectange in the selection
                    .transition()
                    .duration(100)
                    .attr("fill", "rgb(170, 255, 44)"); //hover colour
                    var h = 20;
                  //get tooltip location
                  var xPosition = parseFloat(d3.select(this).attr("x"));
                  var yPosition = parseFloat(d3.select(this).attr("y")) / 100 + h / 7;

            svg.selectAll("path")
                .transition()
                .style("opacity", 0.1);
              d3.select(this)
                .transition()
                    .style("opacity", 1);
                  //update tooltip location and value
                  console.log(d.source.index);
                  d3.select("#tooltip")
                    .style("left", xPosition + "100px")
                    .style("top", yPosition + "3000px")
                    //.select("#value")
                    .text(names[d.source.index] + " received " + d.source.value + " points from " + names[d.target.index]);

                  //use hidden css to hide and show the tooltip
                d3.select("#tooltip").classed("hidden", false);
                })
                .on("mouseout", function(d) { //reset fill on mouse out
                d3.select(this)
                  .transition()
                  .duration(250)
                  .attr("fill", "rgb(0," + Math.round(d+60) + ",0)");
                //hide tooltip
                d3.select("#tooltip").classed("hidden", true);
                });

             function fade(opacity) {
               return function(g, i) {
                 svg.selectAll("g.chord ribbons")
                     .filter(function(d) {
                       return d.source.index != i && d.target.index != i;
                     })
                   .transition()
                     .style("opacity", opacity);

               };
             }
            </script>

如果有人为此提供了可行的解决方案,那将是非常棒的,欢呼:)

1 个答案:

答案 0 :(得分:2)

首先,我要提一下为什么保持选择顺利很重要。例如,这里:

 group.append("path")
      ...
      .on("mouseover", fade(5))
      .on("mouseout", fade(5));

您为鼠标事件应用了侦听器,但是在您之下(您可能正在尝试捕获色带,但我不确定),您用以下方法覆盖了这两个侦听器:

   svg.selectAll("path")
      .on("mouseover", function(d) { ...

通过更复杂的方法(不遵循常规模式),您还会覆盖mouseout的侦听器。只能为特定元素分配一次事件侦听器。因为我们需要为两组(外弧)和丝带设置不同的听众,我们需要改变这种方法。

好消息是,这可以使代码更短更清晰。要重复使用功能区选择,我创建了一个名为ribbons的变量来保存它,变量group将继续保持外弧。

我要打破我看到你要做的三件主要事件:

  1. 不突出所有内容
  2. 这应该非常简单,我们可以使用如下函数:

    function showAllRibbons() {
        ribbons.style("opacity",1)
    }
    
    1. 在鼠标事件上突出显示单个路径:
    2. 这也非常简单,我们隐藏其他所有内容并显示this指向的元素:

      function highlightOneRibbon() {
          ribbons.style("opacity",0.1);
          d3.select(this).style("opacity",1);
      }
      
      1. 突出显示在特定外弧中具有源或目标的所有色带。
      2. 这是最具挑战性的,它有助于查看每个功能区的数据结构,其中包含两个锚点的ID属性:d.source.indexd.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);
        

        当然,你可以获得一点点发烧友,过滤那些不符合标准的淡出它们,过滤那些淡化的东西。

        这个函数更多地证明了为什么你需要为ribbon和arc提供不同的事件监听器,因为这个函数不能使用d3.selectAll("path").on("mouseouver,highlightRibbons),因为我们的索引可能是错误的(因为我们正在选择)丝带也是如此,但这个功能对丝带也没有意义。

        好的,好的。让我们删除你使用d3.selectAll(&#34; path&#34;)的一些代码来操纵已经存在的元素(大约247行以后?)因为这会让我们感到悲伤,而我们是在它,因为调用函数fade的事件监听器被覆盖,我们没有使用它,让我们放弃该功能。

        因此,我失去了工具提示的交互性,但这似乎有一些与问题无关的问题(但同样,组和功能区可能需要单独的监听器,以便工具提示具有相关的信息关于它引用的特征类型。

        这里有一个结果的快速演示,有一些显示组(转换)的额外天赋(因为片段似乎厌恶了大量的代码,我已经修剪了工具提示样式和按钮等因为它们在本演示中不起作用或用于此演示):

        &#13;
        &#13;
        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
                        };
                      });
                    }
        &#13;
        .group-tick line {
                      stroke: #000;
                    }
        
                    .ribbons {
                      fill-opacity: 1;
        
                    }
        &#13;
        <svg width="1000" height="1000"></svg>
                    <script src="https://d3js.org/d3.v4.min.js"></script>
        &#13;
        &#13;
        &#13;