假设我使用 d3-shape 的 curve
部分创建了一些任意路径,通过为其提供一小组 x,y 点,如下所示:
var my_curve = d3.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.curve(d3.curveCatmullRom.alpha(0.5));
var curve_dat = [{x:10,y:10},{x:110,y:120},{x:160,y:20},{x:400,y:60},{x:200,y:360},{x:40,y:300}];
var svg_main = d3.select("div#svg_cont")
.append('svg')
.attr('width', 600)
.attr('height', 400);
var path_1 = svg_main.append('path')
.datum(curve_dat)
.attr('fill', 'none')
.attr('stroke', 'red')
.attr('stroke-width', 3)
.attr('d', my_curve);
svg_main.selectAll('circle')
.data(curve_dat)
.enter()
.append('circle')
.attr('fill', 'none')
.attr('stroke', 'black')
.attr('stroke-width', 3)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 5);
产生:
(其中黑色圆圈是生成点的位置。)
现在,我想沿着路径选择两个点,并从中获取原始路径的部分/子集:
pt1 = path_1.node().getPointAtLength(300);
svg_main.append('circle')
.attr('fill', 'none')
.attr('stroke', 'green')
.attr('stroke-width', 3)
.attr('cx', pt1.x)
.attr('cy', pt1.y)
.attr('r', 5);
pt1 = path_1.node().getPointAtLength(450);
svg_main.append('circle')
.attr('fill', 'none')
.attr('stroke', 'green')
.attr('stroke-width', 3)
.attr('cx', pt1.x)
.attr('cy', pt1.y)
.attr('r', 5);
(其中绿色圆圈是我要获取的部分的边界)
所以即使我有每个点的 x,y,我也不能只绘制由这两个点生成的曲线,因为它与它们之间的原始红色曲线不同。
有没有聪明的方法来做到这一点?通过一些搜索,似乎曾经有一个名为 SVGPathElement.createSVGPathSegClosePath() 的方法。 ,但它已被弃用,他们甚至不提供相关文档。
还有别的办法吗?
最佳答案
严格来说 - 正如 Gerardo 指出的那样,获取路径属性数据以完美复制该曲线非常困难。通常我会保留它,因为他是一位出色的权威。但是,我有点逆势并且喝了一点啤酒,所以让我们看看我们能做些什么。
选项一很简单 - 我们真正需要路径数据有什么用?如果是简单的画图,那么我们可以使用笔划破折号数组。我们使用破折号数组隐藏所有不是我们路径部分的内容:
var my_curve = d3.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.curve(d3.curveCatmullRom.alpha(0.5));
var curve_dat = [{x:10,y:10},{x:110,y:120},{x:160,y:20},{x:400,y:60},{x:200,y:360},{x:40,y:300}];
var svg_main = d3.select("body")
.append('svg')
.attr('width', 600)
.attr('height', 400);
var path_1 = svg_main.append('path')
.datum(curve_dat)
.attr('fill', 'none')
.attr('stroke', 'red')
.attr('stroke-width', 3)
.attr('d', my_curve)
.attr("stroke-dasharray","0 300 150 1000");
svg_main.selectAll('circle')
.data(curve_dat)
.enter()
.append('circle')
.attr('fill', 'none')
.attr('stroke', 'black')
.attr('stroke-width', 3)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 5);
pt1 = path_1.node().getPointAtLength(300);
svg_main.append('circle')
.attr('fill', 'none')
.attr('stroke', 'green')
.attr('stroke-width', 3)
.attr('cx', pt1.x)
.attr('cy', pt1.y)
.attr('r', 5);
pt1 = path_1.node().getPointAtLength(450);
svg_main.append('circle')
.attr('fill', 'none')
.attr('stroke', 'green')
.attr('stroke-width', 3)
.attr('cx', pt1.x)
.attr('cy', pt1.y)
.attr('r', 5);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
我们也可以为它制作动画:
var my_curve = d3.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.curve(d3.curveCatmullRom.alpha(0.5));
var curve_dat = [{x:10,y:10},{x:110,y:120},{x:160,y:20},{x:400,y:60},{x:200,y:360},{x:40,y:300}];
var svg_main = d3.select("body")
.append('svg')
.attr('width', 600)
.attr('height', 400);
var path_1 = svg_main.append('path')
.datum(curve_dat)
.attr('fill', 'none')
.attr('stroke', 'red')
.attr('stroke-width', 3)
.attr('d', my_curve)
.attr("stroke-dasharray","0 300 0 1000")
.transition()
.delay(500)
.duration(2000)
.attr("stroke-dasharray","0 300 150 1000");
svg_main.selectAll('circle')
.data(curve_dat)
.enter()
.append('circle')
.attr('fill', 'none')
.attr('stroke', 'black')
.attr('stroke-width', 3)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 5);
pt1 = path_1.node().getPointAtLength(300);
svg_main.append('circle')
.attr('fill', 'none')
.attr('stroke', 'green')
.attr('stroke-width', 3)
.attr('cx', pt1.x)
.attr('cy', pt1.y)
.attr('r', 5);
pt1 = path_1.node().getPointAtLength(450);
svg_main.append('circle')
.attr('fill', 'none')
.attr('stroke', 'green')
.attr('stroke-width', 3)
.attr('cx', pt1.x)
.attr('cy', pt1.y)
.attr('r', 5);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
选项二不太干净。如果目标是具有路径 d
属性,那么这并不简单,但我们可以通过沿子部分采样点来近似子路径以重新创建它。如果我们使用带有基础曲线的 d3 线或类似的东西,我们可以非常接近:
var my_curve = d3.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.curve(d3.curveCatmullRom.alpha(0.5));
var curve_dat = [{x:10,y:10},{x:110,y:120},{x:160,y:20},{x:400,y:60},{x:200,y:360},{x:40,y:300}];
var svg_main = d3.select("body")
.append('svg')
.attr('width', 600)
.attr('height', 400);
var path_1 = svg_main.append('path')
.datum(curve_dat)
.attr("stroke","steelblue")
.attr('d', my_curve);
svg_main.selectAll('circle')
.data(curve_dat)
.enter()
.append('circle')
.attr('fill', 'none')
.attr('stroke', 'black')
.attr('stroke-width', 3)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('r', 5);
pt1 = path_1.node().getPointAtLength(300);
svg_main.append('circle')
.attr('fill', 'none')
.attr('stroke', 'black')
.attr('stroke-width', 3)
.attr('cx', pt1.x)
.attr('cy', pt1.y)
.attr('r', 5);
pt1 = path_1.node().getPointAtLength(450);
svg_main.append('circle')
.attr('fill', 'none')
.attr('stroke', 'green')
.attr('stroke-width', 3)
.attr('cx', pt1.x)
.attr('cy', pt1.y)
.attr('r', 5);
svg_main.append("path")
.attr("d",getSubPath(path_1))
.attr("stroke","orange");
function getSubPath(path) {
var path = path.node();
var start = 300;
var end = 450;
var every = 5; // sample a point every 5 pixels on the path.
var sampledPoints = d3.range((end-start)/every).map(function(d) {
var p = path.getPointAtLength(start+d*every);
return [p.x,p.y];
})
return d3.line().curve(d3.curveBasis)(sampledPoints);
}
path {
fill:none;
stroke-width: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
采样线是橙色的,原始线是蓝色的,对于大多数意图和目的,这些线是无法区分的(橙色线沿蓝线每 5 个像素采样一次,并带有基本曲线):
关于javascript - 有没有办法在 d3.js 中获取路径的子集/部分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56822311/