javascript - 有没有办法在 d3.js 中获取路径的子集/部分?

标签 javascript d3.js svg

假设我使用 d3-shapecurve 部分创建了一些任意路径,通过为其提供一小组 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);

产生:

enter image description here

(其中黑色圆圈是生成点的位置。)

现在,我想沿着路径选择两个点,并从中获取原始路径的部分/子集:

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);

enter image description here

(其中绿色圆圈是我要获取的部分的边界)

所以即使我有每个点的 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 个像素采样一次,并带有基本曲线):

enter image description here

关于javascript - 有没有办法在 d3.js 中获取路径的子集/部分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56822311/

相关文章:

javascript - 我们如何或可以通过 npm 和 Meteor 使用 Node 模块?

javascript - 使用 .submit() 事件处理程序通过 JQuery 循环表单字段

javascript - D3 水平条形图条未全部出现

java - 如何使用 Glide 将 svg 图像加载到 ImageView

html - 停止 SVG 文件的动画 - 包括代码片段

javascript - 自定义 Soundcloud 可视化工具 webgl

javascript - Google Picker API DocsUploadView setIncludeFolders 不起作用

javascript - 在 d3.js 中合并数组

javascript - 捕获 X 轴值 NVD3 堆积面积图

html - 内有图像的九边形