javascript - d3 v4:仅知道起点和终点时如何使路径弯曲?
问题描述
我想在我成功应用的水平d3 条形图上使用注释。目前,注释通过 a 连接<line>
到相应的条,使用条的结束坐标(也就是因为它是水平条形图width
)和注释的getBBox()
坐标。但是,我希望这条线是弯曲的而不是直线。我知道我需要为此使用 a <path>
,但是当只知道起点和终点时,如何将曲线应用于路径?
仅供参考:我不想对坐标进行硬编码,因为条形图是动画的,并且会不断更改条形的width
.
如何在只知道起点和终点坐标的情况下使路径弯曲?
解决方案
我使用的一个选项是带有 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(...
推荐阅读
- html - 列表中的 CSS max() 宽度
- reactjs - 将 HTTP 重定向到 HTTPS AWS Amplify
- pycharm - 解释“conda update conda”的输出
- amazon-web-services - AWS:尝试运行数据管道。但是,在状态移动到 start 后,它又回到 WAITING_ON_DEPENDENCIES
- r - 如何查看列表的任何元素是否仅包含R中的某个值
- javascript - 新功能,如 VLOOKUP,但基于两个搜索键
- r - 如何在相同的图例中获得线型和颜色?
- homebrew - 通过 brew 升级 pyenv,现在只在新终端上激活系统 python
- c# - EF Core 迁移从其他上下文添加表
- spring-security - ADFS 不在响应消息中发送签名块