d3 v4:如何在只知道起点和终点的情况下使路径弯曲? [英] d3 v4: How to make a path being curved when only start and end point are known?

查看:24
本文介绍了d3 v4:如何在只知道起点和终点的情况下使路径弯曲?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在我成功应用的水平 d3 条形图上使用注释.目前,注释通过 连接到相应的条,使用条的结束坐标(也就是因为它是一个水平条形图,它的 width)和注释的getBBox() 坐标.但是,我希望这条线是弯曲的而不是直线.我知道我需要为此使用 <path> ,但是当只知道起点和终点时,如何将曲线应用于路径?

I want to use annotations on my horizontal d3 bar chart, which I successfully applied. Currently, an annotation is connected through a <line> to the respective bar using the bar's end coordinates (a.k.a. because it is a horizontal bar chart its width) and the annotation's getBBox() coordinates. However, I would like the line to be curved rather than a straight line. I know that I need to use a <path> for that, but how can I apply a curve to a path when only the starting and ending point are known?

仅供参考:我不想对坐标进行硬编码,因为条形图是动画的并且不断更改条形图的width.

FYI: I don't want to hard code the coordinates because the bar chart is animated and keep changes the bar's width.

如何在只知道起点和终点坐标的情况下使路径弯曲?

How can I make the path being curved with only knowing the starting and ending coordinates?

推荐答案

我使用的一个选项是带有 d3 (d3-shape) 的自定义曲线.这允许代码同时使用 Canvas 和 SVG.它还允许使用规定的模式将任意数量的点链接在一起.

One option I've used is a custom curve with d3 (d3-shape). This allows the code to work with both Canvas and SVG. It also allows linking together any number of points with the prescribed pattern.

自定义曲线的文档有点用,但可能更多看一个例子很有用:

The documentation for custom curves is a little useful, but it might be more useful to see an example:

var curve = function(context) {
  var custom = d3.curveLinear(context);
  custom._context = context;
  custom.point = function(x,y) {
    x = +x, y = +y;
    switch (this._point) {
      case 0: this._point = 1; 
        this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
        this.x0 = x; this.y0 = y;        
        break;
      case 1: this._point = 2;
      default: 
        var x1 = this.x0 * 0.5 + x * 0.5;
        var y1 = this.y0 * 0.5 + y * 0.5;
        var m = 1/(y1 - y)/(x1 - x);
        var r = -100; // offset of mid point.
        var k = r / Math.sqrt(1 + (m*m) );
        if (m == Infinity) {
          y1 += r;
        }
        else {
          y1 += k;
          x1 += m*k;
        }     
        this._context.quadraticCurveTo(x1,y1,x,y); 
        this.x0 = x; this.y0 = y;        
        break;
    }
  }
  return custom;
}

这里对于第一个点,我们简单地记录该点,对于后续的点,我们绘制一条从当前点 ([x,y]) 到前一个点 ([x0,y0]) 的二次曲线.在上面的例子中,[x1,y1] 是控制点 - 它偏移了连接 [x,y] 和 [x0,y0] 的直线的垂直量:

Here for the first point we simply record the point, for subsequent points we draw a quadratic curve from the current point ([x,y]) to the previous point ([x0,y0]). In the example above, [x1,y1] is the control point - which is offset a perpendicular amount the line connecting [x,y] and [x0,y0]:

var curve = function(context) {
  var custom = d3.curveLinear(context);
  custom._context = context;
  custom.point = function(x,y) {
    x = +x, y = +y;
    switch (this._point) {
      case 0: this._point = 1; 
        this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y);
        this.x0 = x; this.y0 = y;        
        break;
      case 1: this._point = 2;
      default: 
        var x1 = this.x0 * 0.5 + x * 0.5;
        var y1 = this.y0 * 0.5 + y * 0.5;
        var m = 1/(y1 - y)/(x1 - x);
        var r = -50; // offset of mid point.
        var k = r / Math.sqrt(1 + (m*m) );
        if (m == Infinity || m == -Infinity) {
          y1 += r;
        }
        else {
          y1 += k;
          x1 += m*k;
        }     
        this._context.quadraticCurveTo(x1,y1,x,y); 
        this.x0 = x; this.y0 = y;        
        break;
    }
  }
  return custom;
}

// Basic horizontal bar graph:
var svg = d3.select("body").append("svg")
  .attr("width",500)
  .attr("height", 400);

var data = [5,6,7];

var x = d3.scaleLinear()
  .domain([0,10])
  .range([0,320]);
  
var line = d3.line()
  .curve(curve)
  .x(function(d) { return d[0]; })
  .y(function(d) { return d[1]; })  
  
var g = svg.selectAll("g")
 .data(data)
 .enter()
 .append("g")
 .attr("transform",function(d,i) {
   return "translate("+[40,i*90+30]+")"
 });
 
g.append("rect")
 .attr("width",x)
 .attr("height", 40)

g.append("path")
  .attr("d", function(d,i) {
     return line([[0,-3],[x(d),-3]])
  })

path {
  stroke-width: 1px;
  stroke: black;
  fill:none;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

我会创建一个画布示例,但从线的角度来看原理完全相同,唯一的区别是我们将使用 d3.line().context(context).curve(...

I'd create a canvas example, but the principle is exactly the same from the line perspective, the only difference is we would use d3.line().context(context).curve(...

这篇关于d3 v4:如何在只知道起点和终点的情况下使路径弯曲?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆