【问题标题】:d3 v4: How to make a path being curved when only start and end point are known?d3 v4:仅知道起点和终点时如何使路径弯曲?
【发布时间】:2019-06-30 08:31:40
【问题描述】:

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

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

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

【问题讨论】:

  • 一种选择是使用自定义曲线生成器,如here,它在地图上,但原理和实现基本相同:用一条曲线连接它们的两个点。跨度>
  • @AndrewReid 谢谢!我只需要稍微调整一下,但 curve 函数我可以保留原样。完美的!如果您在此处写此评论作为答案,我将接受它作为所需的答案。
  • 明天早上看答案....

标签: javascript d3.js svg


【解决方案1】:

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

custom curves 的文档有点用处,但看一个例子可能更有用:

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] 的线垂直偏移:

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(...

【讨论】:

    【解决方案2】:

    您可以使用 d 属性绘制 <path>,如 SVG 路径规范 (https://www.w3.org/TR/SVG/paths.html#DProperty) 中所述。

    为此,您可以通过简单地取平均 x 和 y 值并使用Q 二次贝塞尔曲线命令绘制一条线来计算两个点的中点。例如,如果 x1y1 是您的起点,x2y2 是您的终点,则计算中间的 x3y3 并绘制如下:d='M x1,y1 Q x2,y2 x3,y3'

    【讨论】:

      猜你喜欢
      • 2023-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多