D3.js和弦图-使和弦接触拉开的弧线

时间:2018-11-11 08:36:05

标签: javascript d3.js chord-diagram

D3和Javascript新手。我正在制作一个与中心分开的弧线的和弦图。我已经设法将弧线移动到所需的位置,但是我想知道如何使这组和弦再次接触该弧线而不弄乱其他组和弦的位置吗?

这是一张解释我想要的结果的图像。

我当前正在使用if语句选择圆弧C所处的角度并转换其位置。

.attr("transform", function(d) {
            if (d.startAngle > Math.PI*1/2 && d.startAngle < Math.PI*5/6) {
                            return "transform", "translate(" + pullOutSize + "," + pullOutSize + ")"}
        ;})

我也可以通过使用翻译来翻译和弦的位置。

.attr("transform", "translate(" + 50 + "," + 50 + ")")
.attr("d", path);

但是,当我对它应用相同的if语句时,转换将停止工作。好像if语句不符合条件。我不知道将正确的语句放入if语句以帮助我选择要移动的和弦部分。

.attr("transform", function(d) {
            if (d.startAngle > Math.PI*1/2 && d.startAngle < Math.PI*5/6) {
                            return "transform", "translate(" + pullOutSize + "," + pullOutSize + ")"}
        ;})

谢谢!

1 个答案:

答案 0 :(得分:1)

根据文档,您可以在创建和弦/色带时将半径作为参数传递。

ribbon的文档

// from documentation
   var ribbon = d3.ribbon();
     ribbon({
            source: {startAngle: 0.7524114, endAngle: 1.1212972, radius: 240},
            target: {startAngle: 1.8617078, endAngle: 1.9842927, radius: 240}
     }); 

<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
  font: 20px sans-serif;
}

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

.ribbons {
  fill-opacity: 0.67;
}

</style>
<svg width="800" height="800">
<defs>
      <linearGradient id="Gradient1">
        <stop class="stop1" offset="0%"/>
        <stop class="stop2" offset="50%"/>
        <stop class="stop3" offset="100%"/>
      </linearGradient>
      <linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
        <stop offset="0%" stop-color="red"/>
        <stop offset="50%" stop-color="black" stop-opacity="0"/>
        <stop offset="100%" stop-color="blue"/>
      </linearGradient>
  </defs>
</svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>

var matrixData={
  "Occupants":
  [0, 10,10,10,10,0,0,0,10,10,10,10,10],
  "Flooring":
  [70,0,0,0,0,0,30,0,0,0,0,0,0],
  "Millwork":
  [40, 0,0,0,0,0,60,0,0,0,0,0],
  "Ceilings": 
  [60, 0,0,0,0,0,40,0,0,0,0,0,0],
  "Wet Applied Products":
  [60, 0,0,0,0,0,40,0,0,0,0,0,0],
  "Curtain Wall":
  [0, 0,0,0,0,0,100,0,0,0,0,0,0],
  "Environment":
  [0, 10,10,10,10,0,10,0,10,10,10,0,10],
  "Roofing":
  [0, 0,0,0,0,0,80,0,0,0,0,0],
  "Insulation":
  [50, 0,0,0,0,0,50,0,0,0,0,0,0],
  "MEPFP":
  [40, 0,0,0,0,0,60,0,0,0,0,0,0],
  "Structure": 
  [50, 0,0,0,0,0,50,0,0,0,0,0,0],
  "Furniture":
  [90, 0,0,0,0,0,10,0,0,0,0,0,0]
}


matrix= Object.values(matrixData);
matrixKeys = Object.keys(matrixData);
  
var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    outerRadius = Math.min(width, height) * 0.5 - 150,
    innerRadius = outerRadius - 30;

	
	
var formatValue = d3.formatPrefix(",.0", 1e3);

var chord = d3.chord()
    .padAngle(0.024)
    .sortSubgroups(d3.descending);

var arc = d3.arc()
    .innerRadius(innerRadius)
    .outerRadius(outerRadius);

var ribbon = d3.ribbon();

var color = d3.scaleOrdinal()
    .domain(d3.range(4))
    .range(["#6FCDE3", 
            "#D7DAE5", 
            "#D7DAE5", 
            "#D7DAE5", 
            "#D7DAE5", 
            "#D7DAE5", 
            "#E5E52B",
            "#D7DAE5",
            "#D7DAE5", 
            "#D7DAE5", 
            "#D7DAE5", 
            "#D7DAE5"
           ]);

var g = svg.append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ") rotate(75)")
    .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", function(d,i) { 
		var radius = 0;
		if(d.index%2 != 0) radius = 50;
		return arc.innerRadius(innerRadius + radius)
    .outerRadius(outerRadius + radius)(d);}).on("mouseover", function(d,i) {
		fade(.1,d);
	})
    .on("mouseout", function(d,i) {
		fade(1,d);
	});

var groupTick = group.selectAll(".group-tick")
  .data(function(d) { return groupTicks(d, 1e3); })
  .enter().append("g")
    .attr("class", "group-tick")
    .attr("transform", function(d) { 
	  var radius = 0;
	  if(d.index%2 != 0) radius = 50;
      return "rotate(" + (d.angle * 180 / Math.PI - 75) + 
        ") translate(" + (outerRadius + radius) + ",2)"; 
    });

groupTick.append("line")
    .attr("x2", 6);

groupTick
  .filter(function(d) { return d.value % 5e3 === 0; })
  .append("text")
    .attr("x", 8)
    .attr("dy", ".35em")
    .attr("transform", function(d) { return d.angle > Math.PI/2 && d.angle < Math.PI*3/2 ? "rotate(180) translate(-16)" : null; })
    .style("text-anchor", function(d) { return d.angle > Math.PI/2 && d.angle < Math.PI*3/2? "end" : null; })
    .text(function(d) { 
  return matrixKeys[d.index]; 
});

g.append("g")
    .attr("class", "ribbons")
  .selectAll("path")
  .data(function(chords) { 
	return chords; })
  .enter().append("path")
    .attr("d", function(d) {
		if(d.source.index%2 == 0) d.source.radius = innerRadius;
		else d.source.radius = innerRadius + 50;
		if(d.target.index%2 == 0) d.target.radius = innerRadius;
		else d.target.radius = innerRadius + 50;
		return ribbon(d);
	})
	.attr("class", function(d,i) { return matrixKeys[d.index]})
    .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, step) {
  var k = (d.endAngle - d.startAngle) / d.value;
  return d3.range(0, d.value, step).map(function(value) {
    return {
      index:d.index,
      value: value, 
      angle: value * k + d.startAngle
    };
  });
}

function fade(opacity, d2) {
			var hightLight = g.selectAll(".ribbons path").filter(function(d) { 
				return d.source.index != d2.index && d.target.index != d2.index;
			});
			hightLight.transition()
                    .style("opacity", opacity);
        }

</script>