如何绘制管道并使其具有交互性

时间:2014-01-16 16:06:47

标签: d3.js data-visualization

我构建了这个形状(我称之为管道),它是由两个圆圈和一条路径构建的:http://jsfiddle.net/gluz/4udR2/3/embedded/result/ 代码:

    var svg = d3.select("body").append("svg")
    .attr("width", 1200)
    .attr("height", 800);

var circle1 = svg.append("circle").attr("cx", 320).attr("cy", 171).attr("r", 37).style("fill", "#CDDE3A");

var shapeCoordinates = [{"x":254,"y":370},{"x":352,"y":189},{"x":284,"y":162},{"x":235,"y":363}];

var coordinatesFunction = d3.svg.line()
                          .x(function(d) { return d.x; })
                          .y(function(d) { return d.y; })
                         .interpolate("linear-closed");

var lineGraph = svg.append("path")
.attr("d", coordinatesFunction(shapeCoordinates))
                            .attr("stroke", "blue")
                            .attr("stroke-width", 0)
                            .attr("fill", "#CDDE3A")
                            .style("opacity","0.2");

var circle2 = svg.append("circle").attr("cx", 245).attr("cy", 365).attr("r", 10).style("fill", "#CDDE3A").style("opacity", 0.2);

我想让它互动。 对于初学者我希望它看起来它会像一个小圆圈一样构建,如下面的方式,但在第一个例子中有阴影:http://jsfiddle.net/gluz/jeRgz/embedded/result/ 代码:

var svg = d3.select("body").append("svg")
    .attr("width", 1200)
    .attr("height", 800);

var circle1 = svg.append("circle").attr("cx", 245).attr("cy", 365).attr("r", 10).style("fill", "#CDDE3A");

var circle2 = svg.append("circle").attr("cx", 245).attr("cy", 365).attr("r", 10).style("fill", "#CDDE3A").style("opacity", 0.2);

circle1.transition().duration(3000)
    .attr("cx", 320)
    .attr("cy", 171)
    .attr("r", 37)
    .style("opacity", 1.0);

我的问题是: 1.如何将路径对象的圆转换到一起? 2.有没有办法把它建成一个形状? 3.如何让小圆圈不显示它下面的东西,所以它看起来不像是拼接成两个,就像在第一个例子中一样?

谢谢!

1 个答案:

答案 0 :(得分:3)

下面是使用单个path刺痛绘制形状的函数。 Here's a fiddlehere a simpler版本。这里是关于数学的some info,可以使用一些精简。 pt1pt2指定小圆圈和大圆圈的中心,预计是2元素数组, [x,y] r1r2控制每个圆圈的半径。

您可以使用transition()为路径设置动画,但它并不完美,因为弧线参数是线性插值的。更准确的替代方法是通过调用pipePath()来确定如何在每个动画帧重建路径。

function pipePath(pt1, r1, pt2, r2) {
  angle = Math.atan2(
    pt2[0] - pt1[0],
    pt2[1] - pt1[1]
  )
  distance = Math.sqrt(
    Math.pow(pt2[0] - pt1[0], 2) + 
    Math.pow(pt2[1] - pt1[1], 2)
  )
  rDiff = r1 - r2

  if(distance+5 <= r2) { return "M0,0";}

  theta = Math.asin(rDiff/distance)

  l11 = [
    pt1[0] + r1 * Math.cos(theta-angle),
    pt1[1] + r1 * Math.sin(theta-angle)
  ]
  l12 = [
    pt2[0] + r2 * Math.cos(theta-angle),
    pt2[1] + r2 * Math.sin(theta-angle)
  ]
  l21 = [
    pt1[0] + r1 * Math.cos(-theta-angle+Math.PI),
    pt1[1] + r1 * Math.sin(-theta-angle+Math.PI)
  ]
  l22 = [
    pt2[0] + r2 * Math.cos(-theta-angle+Math.PI),
    pt2[1] + r2 * Math.sin(-theta-angle+Math.PI)
  ]
  return "M" + l12 +
    "A" + [r2,r2] + " 0,0,0 " + l22 + // swap 0,0,0 with 0,1,1 for full shape
    "L" + l21 +
    "A" + [r1,r1] + " 0,0,1 " + l11 + "z"
}