d3 v4: Как сделать изогнутый путь, когда известны только начальная и конечная точки? - PullRequest
1 голос
/ 27 июня 2019

Я хочу использовать аннотации на моей горизонтальной d3 гистограмме, которую я успешно применил.В настоящее время аннотация подключается через <line> к соответствующему столбцу с использованием координат конца стержня (иначе говоря, это горизонтальная гистограмма с width) и координат getBBox() аннотации.Однако я бы хотел, чтобы линия была изогнутой, а не прямой.Я знаю, что для этого нужно использовать <path>, но как я могу применить кривую к траектории, когда известны только начальная и конечная точки?

К вашему сведению: Я не хочу жестко кодировать координаты, потому что гистограмма анимирована и сохраняет изменения в width.

Как мне сделатьизогнутый путь, зная только начальную и конечную координаты?

Ответы [ 2 ]

2 голосов
/ 30 июня 2019

Один из вариантов, который я использовал, - это пользовательская кривая с d3 (форма d3). Это позволяет коду работать как с Canvas, так и с SVG. Это также позволяет связать любое количество точек с заданным рисунком.

Документация для пользовательских кривых немного полезна, но может быть более полезным увидеть пример:

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

0 голосов
/ 27 июня 2019

Вы можете нарисовать <path> с атрибутом d, как описано в спецификации пути 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'

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...