Javascript 将SVG路径d属性转换为点数组

Javascript 将SVG路径d属性转换为点数组,javascript,svg,d3.js,Javascript,Svg,D3.js,当我可以创建一行时,如下所示: var lineData = [{ "x": 50, "y": 50 }, {"x": 100,"y": 100}, {"x": 150,"y": 150}, {"x": 200, "y": 200}]; var lineFunction = d3.svg.line() .x(function(d) { return d.x; }) .y(function(d) { return d.y; }) .interpolate("basis"); va

当我可以创建一行时,如下所示:

var lineData = [{ "x": 50, "y": 50 }, {"x": 100,"y": 100}, {"x": 150,"y": 150}, {"x": 200, "y": 200}];
var lineFunction = d3.svg.line()
   .x(function(d) { return d.x; })
   .y(function(d) { return d.y; })
   .interpolate("basis");
var myLine = lineEnter.append("path")
   .attr("d", lineFunction(lineData))
var pathSegList = myLine.node().pathSegList;

var restoredDataset = [];

// loop through segments, adding each endpoint to the restored dataset
for (var i = 0; i < pathSegList.length; i++) {
  restoredDataset.push({
    "x": pathSegList[i].x,
    "y": pathSegList[i].y
  })
}
现在,我想在线性阵列的第二点添加一个文本:

lineEnter.append("text").text("Yaprak").attr("y", function(d){ 
console.log(d); // This is null
console.log("MyLine");
console.log(myLine.attr("d")) // This is the string given below, unfortunately as a String
// return lineData[1].x
return 10;
})

console.log(myLine.attr(“d”))的输出

M50,50L58.3333333333333 2,58.3333333333333333333 2C66.6666666666,66.6666666666,83.3333333333 1,83.3333333333 1,99.99999999999999,99.99999999999999999C116.6666666666666666666666116.6666666666666666666666133.33333333333331133.333333150C133.6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 3L200200

我可以获取字符串格式的路径数据。我可以将此数据转换回lineData数组吗?或者,在追加文本时,是否有其他简单的方法重新生成或获取行数据


请参阅。

您可以通过拆分
L
M
C
字符上的字符串,将行拆分为单独的命令:

var str = "M50,50L58.33333333333332,58.33333333333332C66.66666666666666,
  66.66666666666666,83.33333333333331,83.33333333333331,
  99.99999999999999,99.99999999999999C116.66666666666666,116.66666666666666,
  133.33333333333331,133.33333333333331,150,150C166.66666666666666,
  166.66666666666666,183.33333333333331,183.33333333333331,191.66666666666663,
  191.66666666666663L200,200"

var commands = str.split(/(?=[LMC])/);
这将提供用于渲染路径的命令序列。每个都是一个字符串,由一个字符(L、M或C)后跟一组由逗号分隔的数字组成。它们看起来像这样:

"C66.66666666666666,66.66666666666666,83.33333333333331,
83.33333333333331,99.99999999999999,99.99999999999999"
它描述了一条通过三个点的曲线,[66,66],[83,83]和[99,99]。您可以使用另一个
split
命令和一个包含在映射中的循环将这些点处理为成对点的数组:

var pointArrays = commands.map(function(d){
    var pointsArray = d.slice(1, d.length).split(',');
    var pairsArray = [];
    for(var i = 0; i < pointsArray.length; i += 2){
        pairsArray.push([+pointsArray[i], +pointsArray[i+1]]);
    }
    return pairsArray;
});
与反向工程路径相比,这将是一种更像d3的数据访问方式。也可能更容易理解

API具有获取此信息的内置方法。您不需要自己解析数据字符串

由于您将行的选择存储为变量,因此可以使用
myLine.node()
轻松访问path元素的api,以引用
path
元素本身

例如:

var pathElement = myLine.node();
// THIS USED TO BE CALLED `lineEnter`
var lineGroup = svgContainer.append("g");

var myLine = lineGroup.append("path")
    // HERE IS WHERE YOU BIND THE DATA FOR THE PATH
    .datum(lineData)
    // NOW YOU SIMPLY CALL `lineFunction` AND THE BOUND DATA IS USED AUTOMATICALLY
    .attr("d", lineFunction)
    .attr("stroke", "blue")
    .attr("stroke-width", 2)
    .attr("fill", "none");

// FOR THE LABELS, CREATE AN EMPTY SELECTION
var myLabels = lineGroup.selectAll('.label')
    // FILTER THE LINE DATA SINCE YOU ONLY WANT THE SECOND POINT
    .data(lineData.filter(function(d,i) {return i === 1;})
    // APPEND A TEXT ELEMENT FOR EACH ELEMENT IN THE ENTER SELECTION
    .enter().append('text')
    // NOW YOU CAN USE THE DATA TO SET THE POSITION OF THE TEXT
    .attr('x', function(d) {return d.x;})
    .attr('y', function(d) {return d.y;})
    // FINALLY, ADD THE TEXT ITSELF
    .text('Yaprak')
然后,通过访问
pathSegList
属性,可以访问用于构建路径的命令列表:

var pathSegList = pathElement.pathSegList;
使用此对象的
length
属性,您可以轻松地在其中循环,以获得与每个路径段关联的坐标:

for (var i = 0; i < pathSegList.length; i++) {
  console.log(pathSegList[i]);
}
编辑:

至于在追加文本时使用原始数据。。。我假设您正在寻找将标签附加到点上,不需要经历所有重建数据的麻烦。事实上,真正的问题是,您从来没有使用数据绑定来制作线图。尝试对路径使用
.datum()
方法绑定数据,对标签使用
.data()
方法绑定数据。另外,您可能需要重命名
lineEnter
,因为您没有使用enter选择,它只是表示一个组。例如:

var pathElement = myLine.node();
// THIS USED TO BE CALLED `lineEnter`
var lineGroup = svgContainer.append("g");

var myLine = lineGroup.append("path")
    // HERE IS WHERE YOU BIND THE DATA FOR THE PATH
    .datum(lineData)
    // NOW YOU SIMPLY CALL `lineFunction` AND THE BOUND DATA IS USED AUTOMATICALLY
    .attr("d", lineFunction)
    .attr("stroke", "blue")
    .attr("stroke-width", 2)
    .attr("fill", "none");

// FOR THE LABELS, CREATE AN EMPTY SELECTION
var myLabels = lineGroup.selectAll('.label')
    // FILTER THE LINE DATA SINCE YOU ONLY WANT THE SECOND POINT
    .data(lineData.filter(function(d,i) {return i === 1;})
    // APPEND A TEXT ELEMENT FOR EACH ELEMENT IN THE ENTER SELECTION
    .enter().append('text')
    // NOW YOU CAN USE THE DATA TO SET THE POSITION OF THE TEXT
    .attr('x', function(d) {return d.x;})
    .attr('y', function(d) {return d.y;})
    // FINALLY, ADD THE TEXT ITSELF
    .text('Yaprak')

有点老套,但可以使用animateMotion沿路径为对象(例如,矩形或圆形)设置动画,然后对对象的x/y位置进行采样。您必须做出一系列选择(例如,设置对象动画的速度有多快,采样x/y位置的速度有多快,等等)。您还可以多次运行此过程,并获取某种平均值或中值

完整代码(请参见操作中的代码:)


/**
*将路径转换为点数组。
*
*使用animateMotion和setInterval从路径“窃取”点。
*它非常粗糙,我不知道它工作得有多好。
*
*@param svgpatheElement要转换的路径
*@param int要读取的近似点数
*一旦数据准备就绪,就会调用@param callback
*/
函数路径点(路径、分辨率、onDone){
var ctx={};
分辨率=分辨率;
ctx.onDone=onDone;
ctx.points=[];
ctx.interval=null;
//向上遍历节点,直到找到根svg节点
var svg=路径;
while(!(SVGSVGElement的svg实例)){
svg=svg.parentElement;
}
//创建一个rect,用于跟踪路径
var rect=document.createElements(“http://www.w3.org/2000/svg“,“rect”);
ctx.rect=rect;
appendChild(rect);
var motion=document.createElements(“http://www.w3.org/2000/svg","情感";;
motion.setAttribute(“path”,path.getAttribute(“d”);
motion.setAttribute(“开始”、“0”);
motion.setAttribute(“dur”,“3”);//TODO:将其设置为更大的值,例如10秒?
motion.setAttribute(“repeatCount”、“1”);
motion.onbegin=PathToPoints.beginRecording.bind(this,ctx);
motion.onend=PathToPoints.stopRecording.bind(this,ctx);
//添加矩形
直接附加子对象(运动);
}
PathToPoints.beginRecording=函数(ctx){
var m=ctx.rect.getScreenCTM();
点推力({x:m.e,y:m.f});
ctx.interval=setInterval(PathToPoints.recordPosition.bind(this,ctx),1000*3/ctx.resolution);
}
PathToPoints.stopRecording=函数(ctx){
clearInterval(ctx.interval);
//移除矩形
ctx.rect.remove();
ctx.onDone(ctx.points);
}
PathToPoints.recordPosition=函数(ctx){
var m=ctx.rect.getScreenCTM();
点推力({x:m.e,y:m.f});
}
PathToPoints(mypath,100,函数(p){points.textContent=JSON.stringify(p)});

我在谷歌上找到了这个问题。我需要的只是SVG
path
对象的
pathSegList
属性:

var points = pathElement.pathSegList;
每一点看起来都像

y: 57, x: 109, pathSegTypeAsLetter: "L", pathSegType: 4, PATHSEG_UNKNOWN: 0…}


pathSegList
在旧Chrome中受支持,并从Chrome 48中删除。
但是Chrome还没有实现这个功能

用于处理旧API。

用于使用。建议使用

var path = myLine.node();
//Be sure you have added the pathdata polyfill to your page before use getPathData
var pathdata = path.getPathData();
console.log(pathdata);
//you will get an Array object contains all path data details
//like this:
[
    {
        "type": "M",
        "values": [ 50, 50 ]
    },
    {
        "type": "L",
        "values": [ 58.33333333333332, 58.33333333333332 ]
    },
    {
        "type": "C",
        "values": [ 66.66666666666666, 66.66666666666666, 83.33333333333331, 83.33333333333331, 99.99999999999999, 99.99999999999999 ]
    },
    {
        "type": "C",
        "values": [ 116.66666666666666, 116.66666666666666, 133.33333333333331, 133.33333333333331, 150, 150 ]
    },
    {
        "type": "C",
        "values": [ 166.66666666666666, 166.66666666666666, 183.33333333333331, 183.33333333333331, 191.66666666666663, 191.66666666666663 ]
    },
    {
        "type": "L",
        "values": [ 200, 200 ]
    }
]

我已经成功地使用它来渲染x,y点列表:

代码超出了这个文本框所能容纳的范围,但这里可能是一个良好的开端:

这将是一个问题,因为