【问题标题】:Drawing bezierCurve in d3.js在 d3.js 中绘制 bezierCurve
【发布时间】:2020-06-03 04:54:55
【问题描述】:

如何在 d3.js 中使用 bezierCurveTo 方法绘制线条,使线条如下图所示

我刚刚提到了bezier Curve,但我对此一无所知。

【问题讨论】:

  • 有linkHorizo​​ntal(见my answer here),但不能更改控制点。因此,如果您想要精确的形状,只需自己创建 d 属性即可。
  • 谢谢 Gerardo :) 虽然我正在尝试使用 dagre-d3。即使我按照建议尝试使用 BeziereCurve (github.com/dagrejs/dagre-d3/issues/305)。对此有什么想法吗?
  • 不,我不知道 dagre-d3。正如我所说,只需自己创建 "d" 属性,这将比找到库/插件花费更少的时间。
  • 好的 :) 让我试试
  • 您可以尝试使用这个 d 属性 d="M50,180 L40,180 Q30,180 30,170L30,110.000 Q30,100 20,100L10,100 Q0,100 10,100L10,100 Q0,100 10,100L20,100 Q30,100 30,90L30,20 Q30,10 40,10L50,10" 但一切都取决于您需要什么(svg 元素的大小、路径的位置等...

标签: d3.js svg


【解决方案1】:

有很多方法可以做到这一点。可以制作一个custom curve 来实现这一点。

但是,我们也可以让它更简单。从 d3 布局传递给链接生成器(例如 d3.linkHorizo​​ntal)的数据通常包含源和目标属性,其中每个属性通常包含 x 和 y 属性。假设这种结构,我们可以创建一个函数,使用这些函数并创建并返回带有贝塞尔曲线的适当路径数据:

var linker = function(d) {
  var x0 = d.source.x;
  var y0 = d.source.y;
  var y1 = d.target.y;
  var x1 = d.target.x;
  var k = 120;
  
  var path = d3.path()
  path.moveTo(y0,x0)
  path.bezierCurveTo(y1-k,x0,y0,x1,y1-k,x1);
  path.lineTo(y1,x1);
  
  return path.toString();
}

以上内容非常基本,它使用d3.path,但您可以轻松地自己构建 SVG 路径字符串。网上有很多交互式贝塞尔曲线浏览器,因此您可以找出最有效的控制点。 由于我使用的树布局是垂直的,因此我通过反转 x 和 y 将其变为水平,这就是为什么我的坐标是 [y,x]。我使用上面的k 将贝塞尔曲线偏移到左侧整体链接的一小部分:

但是你可以很容易地用这个把曲线放在链接的中间:

下面是实际操作:

var data = { "name": "Parent", "children": [ 
    { "name": "Child A", "children": [ { "name": "Grandchild1"}, {"name":"Grandchild2" } ] }, 
    { "name": "Child B", } 
    ] };

var width = 400;
var height = 300;

margin = {left: 50, top: 10, right:30, bottom: 10}

var svg = d3.select("body").append("svg")
   .attr("width", width)
   .attr("height", height);
      
var g = svg.append("g").attr('transform','translate('+ margin.left +','+ margin.right +')');

var root = d3.hierarchy(data);
      
var tree = d3.tree()
   .size([height-margin.top-margin.bottom,width-margin.left-margin.right]);
   
var linker = function(d) {
  var x0 = d.source.x;
  var y0 = d.source.y;
  var y1 = d.target.y;
  var x1 = d.target.x;
  var k = (y1-y0)/2;
  
  var path = d3.path()
  path.moveTo(y0,x0)
  path.lineTo(y0+k/2,x0)
  path.bezierCurveTo(y1-k,x0,y0+k,x1,y1-k/2,x1);
  path.lineTo(y1,x1);
  
  return path.toString();
}

 var link = g.selectAll(".link")
    .data(tree(root).links())
    .enter().append("path")
      .attr("class", "link")
      .attr("d", linker);

  var node = g.selectAll(".node")
    .data(root.descendants())
    .enter().append("g")
      .attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

  node.append("circle")
      .attr("r", 2.5);
      
  node.append("text")
     .text(function(d) { return d.data.name; })
     .attr('y',-10)
     .attr('x',-10)
     .attr('text-anchor','middle');
.node circle {
          fill: #fff;
          stroke: steelblue;
          stroke-width: 3px;
        }

        .link {
          fill: none;
          stroke: #ccc;
          stroke-width: 2px;
        }
<script src="https://d3js.org/d3.v4.min.js"></script>

但是,在阅读 cmets 时,我注意到您的问题可能更多是关于 dagreD3 - 这会大大改变事情。 Dagre D3 相对于 D3 提供了更好的易用性,但代价是一些 D3灵活性。如果您想为 DagreD3 提供某种类型的曲线,那么您应该使用 d3 曲线或一些自定义曲线(如上面的链接答案)。您可以在添加边时很容易地指定曲线。

但这并不能解决边缘源自图像中同一点的问题。我将提供一个基于 d3 的解决方案——它可能会破坏边缘标签的放置、转换等——所以如果你需要那个功能,它应该被构建出来。我将使用上面的贝塞尔生成器。以下内容受this启发:

var g = new dagreD3.graphlib.Graph()
  .setGraph({rankdir: 'LR'})
  .setDefaultEdgeLabel(function() { return {}; });

g.setNode(0,  { label: "0"});
g.setNode(1,  { label: "1"});
g.setNode(2,  { label: "2"});
g.setNode(3,  { label: "3"});
g.setNode(4,  { label: "4"});

g.setEdge(0, 1);
g.setEdge(0, 2);
g.setEdge(1, 3);
g.setEdge(1, 4);

var render = new dagreD3.render().createEdgePaths(createEdgePaths);


var svg = d3.select("svg"),
    svgGroup = svg.append("g"),
    zoom = d3.zoom().on("zoom", function() {
      svgGroup.attr("transform", d3.event.transform);
    });
svg.call(zoom);

render(svgGroup, g);

function createEdgePaths(selection, g, arrows) {
   selection.selectAll("g.edgePath")
    .data(g.edges())
    .enter()
    .append("path")
    .attr("d", function(e) {
      return calcPoints(g,e);  
    });
}

function calcPoints(g, e) {
  var source = g.node(e.v);
  var target = g.node(e.w);
  var x0 = source.x + source.width/2;
  var x1 = target.x - target.width/2;
  var y0 = source.y;
  var y1 = target.y;
  return linker(x0,y0,x1,y1)
}
function linker(x0,y0,x1,y1) {
 var dx = x1 -x0;
 var k = dx/3;
      
 var path = d3.path()
 path.moveTo(x0,y0)
 path.bezierCurveTo(x1-k,y0,x0,y1,x1-k,y1);
 path.lineTo(x1,y1);
      
 return path.toString();
}
path {
  stroke: #333;
  stroke-width: 1.5px;
  fill: none;
}
rect {
  fill: none;
  stroke:black;
  stroke-width: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dagre-d3@0.6.1/dist/dagre-d3.min.js"></script>
<svg width="800" height="600"></svg>

【讨论】:

  • :) 谢谢安德鲁·里德 +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-24
  • 2015-01-01
  • 2018-06-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多