d3 + vx 反应图表曲线在每个数据点平坦 [英] d3 + vx react chart curve flat at each data point

查看:23
本文介绍了d3 + vx 反应图表曲线在每个数据点平坦的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试按照

图片来自这个codepen

为了实现这个想法,我们可以用绘制贝塞尔曲线的函数替换标准曲线中的点函数,例如 d3.curveLinear.

用于比较的d3.curveLinear的点函数:

 function(x, y) {x = +x, y = +y;开关(this._point){案例 0: this._point = 1;this._line ?this._context.lineTo(x, y) : this._context.moveTo(x, y);休息;情况1:this._point = 2;//继续默认值:this._context.lineTo(x, y);休息;}

还有新的点函数:

 function(x,y) {x = +x, y = +y;开关(this._point){案例 0: this._point = 1;this._line ?this._context.lineTo(x, y) : this._context.moveTo(x, y);this.x0 = x;this.y0 = y;休息;情况1:this._point = 2;默认:var x1 = this.x0 * 0.5 + x * 0.5;this._context.bezierCurveTo(x1,this.y0,x1,y,x,y);//bezierCurveTo(controlPoint1X,controlPoint1Y,controlPoint2X,controlPoint2Y,endPointX,endPointY)this.x0 = x;this.y0 = y;休息;}}退换货;}

我对每个控制点使用相同的 x 值,这可能不太理想,因为它们的末端可能不够平坦,但很容易改变

我们可以通过使用 d3.curveLinear 并代入这个新的点函数来创建自定义曲线:

var 曲线 = 函数(上下文){var custom = d3.curveLinear(context);custom._context = 上下文;自定义点 = 函数(x,y){x = +x, y = +y;开关(this._point){案例 0: this._point = 1;this._line ?this._context.lineTo(x, y) : this._context.moveTo(x, y);this.x0 = x;this.y0 = y;休息;情况1:this._point = 2;默认:var x1 = this.x0 * 0.5 + x * 0.5;this._context.bezierCurveTo(x1,this.y0,x1,y,x,y);this.x0 = x;this.y0 = y;休息;}}退换货;}

这很容易工作:

var curve = function(context) {var custom = d3.curveLinear(context);custom._context = 上下文;自定义点 = 函数(x,y){x = +x, y = +y;开关(this._point){案例 0: this._point = 1;this._line ?this._context.lineTo(x, y) : this._context.moveTo(x, y);this.x0 = x;this.y0 = y;休息;情况1:this._point = 2;默认:var x1 = this.x0 * 0.5 + x * 0.5;this._context.bezierCurveTo(x1,this.y0,x1,y,x,y);this.x0 = x;this.y0 = y;休息;}}退换货;}变量数据 = [[10,10],[160,50],[310,100]];var line = d3.line().curve(曲线);d3.select("svg").append("路径").attr("d",line(data));d3.select("svg").selectAll("圆").data(数据).进入().append("圆圈").attr(转换",函数(d){return "translate("+d+")";}).attr("r",3)

path {填充:无;笔画:黑色;笔画宽度:1px;}

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

这里有一个区域演示,为每个阶段绘制了一个漏斗.根据您的数据,这可能会或可能不会更可取.我的假数据是按阶段构建的,因此最容易单独绘制每个阶段.将所有阶段统一到一个路径/区域中可能会有点困难.

var curve = function(context) {var custom = d3.curveLinear(context);custom._context = 上下文;自定义点 = 函数(x,y){x = +x, y = +y;开关(this._point){案例 0: this._point = 1;this._line ?this._context.lineTo(x, y) : this._context.moveTo(x, y);this.x0 = x;this.y0 = y;休息;情况1:this._point = 2;默认:var x1 = this.x0 * 0.5 + x * 0.5;this._context.bezierCurveTo(x1,this.y0,x1,y,x,y);this.x0 = x;this.y0 = y;休息;}}退换货;}变量数据 = [{阶段:1,开始:1,结束:0.5},{阶段:2,开始:0.5,结束:0.2},{阶段:3,开始:0.2,结束:0.1},{阶段:4,开始:0.1,结束:0.005}]var yRangeMax = 100;var y = d3.scaleLinear().range([yRangeMax,0]);var x = d3.scaleBand().range([50,400]).domain(data.map(function(d) {返回 d.stage;}))var svg = d3.select("svg");var area = d3.area().curve(曲线).y1(function(d) { return yRangeMax*2-d[1]; }).y0(function(d) { return d[1]; })svg.selectAll(null).data(数据).进入().append("路径").attr(d",函数(d){var p1 = [x(d.stage),y(d.start)]var p2 = [x(d.stage)+x.step(),y(d.end)]返回区域([p1,p2])})

path{中风:#bbb;笔画宽度:1px;填写:#ccc;}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.6.0/d3.min.js"></script><svg width="600" height="400"></svg>

至于自定义曲线的指导,我看到的资源并不多.但是,这些答案(ab) 可能会有所帮助.

I am trying to build something along the lines of https://dribbble.com/shots/2673159-Funnel-UI-concept/attachments/538068

I've looked through all the curves provided by d3/vx and none of them seem to have the curve happen between each distinct step with the line flat at the data point. Is there a curve type i'm missing that would look similar to the above? If not and it needs a custom curve, is there somewhere with a more in-depth description of how to implement custom curves than the d3 docs?

解决方案

There is no standard curve that does this; however, it may be possible if duplicating adding additional points to coerce a standard d3 curve to be flat where needed. For example, adding points before and after the stage change with the same y value.

However, a custom curve could work and avoid the need for data manipulation. A bezier curve should do the trick. With origin, destination, and control points something like:

Image from this codepen

To implement this idea we can replace the point function in a standard curve, such as d3.curveLinear with one that draws a bezier curve.

The point function of d3.curveLinear for comparison:

  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); break;
      case 1: this._point = 2; // proceed
      default: this._context.lineTo(x, y); break;
    }

And the new point function:

  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;
        this._context.bezierCurveTo(x1,this.y0,x1,y,x,y); // bezierCurveTo(controlPoint1X,controlPoint1Y,controlPoint2X,controlPoint2Y,endPointX,endPointY)
        this.x0 = x; this.y0 = y;        
        break;
    }
  }
  return custom;
}

I'm using the same x value for each control point, this might not be ideal as they may not be flat enough at the ends, but it is easily changed

We can create a custom curve by using d3.curveLinear and substituting in this new point function:

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;
        this._context.bezierCurveTo(x1,this.y0,x1,y,x,y); 
        this.x0 = x; this.y0 = y;        
        break;
    }
  }
  return custom;
}

This works easily enough:

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;
        this._context.bezierCurveTo(x1,this.y0,x1,y,x,y);
        this.x0 = x; this.y0 = y;        
        break;
    }
  }
  return custom;
}

var data = [
 [10,10],
 [160,50],
 [310,100]
];

var line = d3.line()
  .curve(curve);
  
d3.select("svg")
  .append("path")
  .attr("d",line(data));
  
d3.select("svg")
  .selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("transform", function(d) {
    return "translate("+d+")";
  })
  .attr("r",3)

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

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

And here's an areal demonstration that draws a funnel for each stage. Depending on your data this may or may not be preferable. My fake data is structured by stage, so it is easiest to draw each stage individually. Unifying all the stages into one path/area may be a bit more difficult.

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;
        this._context.bezierCurveTo(x1,this.y0,x1,y,x,y);
        this.x0 = x; this.y0 = y;        
        break;
    }
  }
  return custom;
}

var data = [
  {stage:1, start:1,end:0.5},
  {stage:2, start:0.5,end:0.2},
  {stage:3, start:0.2,end:0.1},
  {stage:4, start:0.1,end:0.005}
]

var yRangeMax = 100;
var y = d3.scaleLinear()
  .range([yRangeMax,0]);
  
var x = d3.scaleBand()
  .range([50,400])
  .domain(data.map(function(d) {
    return d.stage;
  }))

var svg = d3.select("svg");
 
var area = d3.area()
  .curve(curve)
  .y1(function(d) { return yRangeMax*2-d[1]; })
  .y0(function(d) { return d[1]; })

svg.selectAll(null)
  .data(data)
  .enter()
  .append("path")
  .attr("d", function(d) {
    var p1 = [x(d.stage),y(d.start)]
    var p2 = [x(d.stage)+x.step(),y(d.end)]
    return area([p1,p2])
  })

path{
  stroke: #bbb;
  stroke-width: 1px;
  fill:#ccc;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.6.0/d3.min.js"></script>
<svg width="600" height="400"></svg>

As for guidance on custom curves, there are not a lot of resources that I've seen. However, these answers (a,b) might be of some assistance.

这篇关于d3 + vx 反应图表曲线在每个数据点平坦的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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