如何获取 svg 元素的坐标?

Posted

技术标签:

【中文标题】如何获取 svg 元素的坐标?【英文标题】:How to get coordinates of an svg element? 【发布时间】:2013-10-09 21:10:26 【问题描述】:

我正在使用 d3 从相对 svg 位置绘制一条线,因此想要访问元素本身的坐标。我尝试过这样的事情(其中“this”指的是元素)

 .attr("x1", this.x)
 .attr("y1", this.y)

但 x1 和 y1 只是默认为零。获取svg元素坐标的正确方法是什么?

编辑:

这是我的(根据要求)我的整个代码。我正在尝试在可折叠树上实现这一点,当我将鼠标悬停在圆形节点上时,我会在其中画一条线。线的起点应该是我点击的圆,终点应该是svg平面上的一个固定点。

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <script type="text/javascript" src="d3.v3.js"></script>
    <script type="text/javascript" src="d3.layout.js"></script>
    <style type="text/css">

.node circle 
  cursor: pointer;
  fill: #fff;
  stroke: steelblue;
  stroke-width: 1.5px;


.node text 
  font: 10px sans-serif;


path.link 
  fill: none;
  stroke: #ccc;
  stroke-width: 1.5px;


    </style>
  </head>
  <body>
    <div id="chart"></div>
    <script type="text/javascript">

var m = [20, 120, 20, 120],
    w = 2240 - m[1] - m[3],
    h = 800 - m[0] - m[2],
    i = 0,
    duration = 500,
    root;

var tree = d3.layout.tree()
    .size([h, w]);

var diagonal = d3.svg.diagonal()
    .projection(function(d)  return [d.y, d.x]; );

var vis = d3.select("#chart").append("svg")
    .attr("width", w + m[1] + m[3])
    .attr("height", h + m[0] + m[2])
  .append("g")
    .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

function loadData(json) 
    root = json;
    d3.select("#processName").html(root.text);
    root.x0 = h / 2;
    root.y0 = 0;
    root.children.forEach(collapse);
    update(root);  


function collapse(d) 
    if (d.children) 
      d._children = d.children;
      d._children.forEach(collapse);
      d.children = null;
    
 ;

// Toggle children on click.
function click(d) 
  if (d.children) 
    d._children = d.children;
    d.children = null;
   else 
    d.children = d._children;
    d._children = null;
  
  update(d);


function update(source) 

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse();

  // Normalize for fixed-depth.
  nodes.forEach(function(d)  d.y = d.depth * 180; );

  // Update the nodes…
  var node = vis.selectAll("g.node")
      .data(nodes, function(d)  return d.id || (d.id = ++i); );

  // Enter any new nodes at the parent's previous position.
  var nodeEnter = node.enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d)  return "translate(" + source.y0 + "," + source.x0 + ")"; )
      .on("click", click);

  nodeEnter.append("circle")
      .attr("r", 1e-6)
      .style("fill", function(d)  return d._children ? "lightsteelblue" : "#fff"; )
    .on('mouseover', function(d, i)
        var dataset = [1,2,3,4]                                    // HERE
        vis.selectAll("line")                                      // HERE
            .data(dataset)                                 // HERE
            .enter()                                       // HERE
            .append("line")                                // HERE
            .attr("x1", this.x)                            // HERE'S THE PROBLEM FOR PERRY
            .attr("y1", this.y)                            // HERE'S THE PROBLEM FOR PERRY
            .attr("x2", 500)                               // HERE
            .attr("y2", 500)                               // HERE
            .style("stroke", "rgb(6,120,155)");           // HERE
       ); 

  nodeEnter.append("text")
      .attr("x", function(d)  return d.children || d._children ? -10 : 10; )
      .attr("dy", ".35em")
      .attr("text-anchor", function(d)  return d.children || d._children ? "end" : "start"; )
      .text(function(d)  return d.name; )
      .style("fill-opacity", 1e-6);

  // Transition nodes to their new position.
  var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d)  return "translate(" + d.y + "," + d.x + ")"; );

  nodeUpdate.select("circle")
      .attr("r", 4.5)
      .style("fill", function(d)  return d._children ? "lightsteelblue" : "#fff"; );

  nodeUpdate.select("text")
      .style("fill-opacity", 1);

  // Transition exiting nodes to the parent's new position.
  var nodeExit = node.exit().transition()
      .duration(duration)
      .attr("transform", function(d)  return "translate(" + source.y + "," + source.x + ")"; )
      .remove();

  nodeExit.select("circle")
      .attr("r", 1e-6);

  nodeExit.select("text")
      .style("fill-opacity", 1e-6);

  // Update the links…
  var link = vis.selectAll("path.link")
      .data(tree.links(nodes), function(d)  return d.target.id; );

  // Enter any new links at the parent's previous position.
  link.enter().insert("path", "g")
      .attr("class", "link")
      .attr("d", function(d) 
        var o = x: source.x0, y: source.y0;
        return diagonal(source: o, target: o);
      )
    .transition()
      .duration(duration)
      .attr("d", diagonal);

  // Transition links to their new position.
  link.transition()
      .duration(duration)
      .attr("d", diagonal);

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
      .duration(duration)
      .attr("d", function(d) 
        var o = x: source.x, y: source.y;
        return diagonal(source: o, target: o);
      )
      .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) 
    d.x0 = d.x;
    d.y0 = d.y;
  );

;

loadData(
 "name": "flare",
 "children": [
  
   "name": "analytics",
   "children": [
    
     "name": "cluster",
     "children": [
      "name": "AgglomerativeCluster", "size": 3938,
      "name": "CommunityStructure", "size": 3812,
      "name": "HierarchicalCluster", "size": 6714,
      "name": "MergeEdge", "size": 743
     ]
    ,
    
     "name": "graph",
     "children": [
      "name": "BetweennessCentrality", "size": 3534,
      "name": "LinkDistance", "size": 5731,
      "name": "MaxFlowMinCut", "size": 7840,
      "name": "ShortestPaths", "size": 5914,
      "name": "SpanningTree", "size": 3416
     ]
    ,
    
     "name": "optimization",
     "children": [
      "name": "AspectRatioBanker", "size": 7074
     ]
    
   ]
  ,
  
   "name": "animate",
   "children": [
    "name": "Easing", "size": 17010,
    "name": "FunctionSequence", "size": 5842,
    
     "name": "interpolate",
     "children": [
      "name": "ArrayInterpolator", "size": 1983,
      "name": "ColorInterpolator", "size": 2047,
      "name": "DateInterpolator", "size": 1375,
      "name": "Interpolator", "size": 8746,
      "name": "MatrixInterpolator", "size": 2202,
      "name": "NumberInterpolator", "size": 1382,
      "name": "ObjectInterpolator", "size": 1629,
      "name": "PointInterpolator", "size": 1675,
      "name": "RectangleInterpolator", "size": 2042
     ]
    ,
    "name": "ISchedulable", "size": 1041,
    "name": "Parallel", "size": 5176,
    "name": "Pause", "size": 449,
    "name": "Scheduler", "size": 5593,
    "name": "Sequence", "size": 5534,
    "name": "Transition", "size": 9201,
    "name": "Transitioner", "size": 19975,
    "name": "TransitionEvent", "size": 1116,
    "name": "Tween", "size": 6006
   ]
  ,
  
   "name": "data",
   "children": [
    
     "name": "converters",
     "children": [
      "name": "Converters", "size": 721,
      "name": "DelimitedTextConverter", "size": 4294,
      "name": "GraphMLConverter", "size": 9800,
      "name": "IDataConverter", "size": 1314,
      "name": "JSONConverter", "size": 2220
     ]
    ,
    "name": "DataField", "size": 1759,
    "name": "DataSchema", "size": 2165,
    "name": "DataSet", "size": 586,
    "name": "DataSource", "size": 3331,
    "name": "DataTable", "size": 772,
    "name": "DataUtil", "size": 3322
   ]
  ,
  
   "name": "display",
   "children": [
    "name": "DirtySprite", "size": 8833,
    "name": "LineSprite", "size": 1732,
    "name": "RectSprite", "size": 3623,
    "name": "TextSprite", "size": 10066
   ]
  ,
  
   "name": "flex",
   "children": [
    "name": "FlareVis", "size": 4116
   ]
  ,
  
   "name": "physics",
   "children": [
    "name": "DragForce", "size": 1082,
    "name": "GravityForce", "size": 1336,
    "name": "IForce", "size": 319,
    "name": "NBodyForce", "size": 10498,
    "name": "Particle", "size": 2822,
    "name": "Simulation", "size": 9983,
    "name": "Spring", "size": 2213,
    "name": "SpringForce", "size": 1681
   ]
  ,
  
   "name": "query",
   "children": [
    "name": "AggregateExpression", "size": 1616,
    "name": "And", "size": 1027,
    "name": "Arithmetic", "size": 3891,
    "name": "Average", "size": 891,
    "name": "BinaryExpression", "size": 2893,
    "name": "Comparison", "size": 5103,
    "name": "CompositeExpression", "size": 3677,
    "name": "Count", "size": 781,
    "name": "DateUtil", "size": 4141,
    "name": "Distinct", "size": 933,
    "name": "Expression", "size": 5130,
    "name": "ExpressionIterator", "size": 3617,
    "name": "Fn", "size": 3240,
    "name": "If", "size": 2732,
    "name": "IsA", "size": 2039,
    "name": "Literal", "size": 1214,
    "name": "Match", "size": 3748,
    "name": "Maximum", "size": 843,
    
     "name": "methods",
     "children": [
      "name": "add", "size": 593,
      "name": "and", "size": 330,
      "name": "average", "size": 287,
      "name": "count", "size": 277,
      "name": "distinct", "size": 292,
      "name": "div", "size": 595,
      "name": "eq", "size": 594,
      "name": "fn", "size": 460,
      "name": "gt", "size": 603,
      "name": "gte", "size": 625,
      "name": "iff", "size": 748,
      "name": "isa", "size": 461,
      "name": "lt", "size": 597,
      "name": "lte", "size": 619,
      "name": "max", "size": 283,
      "name": "min", "size": 283,
      "name": "mod", "size": 591,
      "name": "mul", "size": 603,
      "name": "neq", "size": 599,
      "name": "not", "size": 386,
      "name": "or", "size": 323,
      "name": "orderby", "size": 307,
      "name": "range", "size": 772,
      "name": "select", "size": 296,
      "name": "stddev", "size": 363,
      "name": "sub", "size": 600,
      "name": "sum", "size": 280,
      "name": "update", "size": 307,
      "name": "variance", "size": 335,
      "name": "where", "size": 299,
      "name": "xor", "size": 354,
      "name": "_", "size": 264
     ]
    ,
    "name": "Minimum", "size": 843,
    "name": "Not", "size": 1554,
    "name": "Or", "size": 970,
    "name": "Query", "size": 13896,
    "name": "Range", "size": 1594,
    "name": "StringUtil", "size": 4130,
    "name": "Sum", "size": 791,
    "name": "Variable", "size": 1124,
    "name": "Variance", "size": 1876,
    "name": "Xor", "size": 1101
   ]
  ,
  
   "name": "scale",
   "children": [
    "name": "IScaleMap", "size": 2105,
    "name": "LinearScale", "size": 1316,
    "name": "LogScale", "size": 3151,
    "name": "OrdinalScale", "size": 3770,
    "name": "QuantileScale", "size": 2435,
    "name": "QuantitativeScale", "size": 4839,
    "name": "RootScale", "size": 1756,
    "name": "Scale", "size": 4268,
    "name": "ScaleType", "size": 1821,
    "name": "TimeScale", "size": 5833
   ]
  ,
  
   "name": "util",
   "children": [
    "name": "Arrays", "size": 8258,
    "name": "Colors", "size": 10001,
    "name": "Dates", "size": 8217,
    "name": "Displays", "size": 12555,
    "name": "Filter", "size": 2324,
    "name": "Geometry", "size": 10993,
    
     "name": "heap",
     "children": [
      "name": "FibonacciHeap", "size": 9354,
      "name": "HeapNode", "size": 1233
     ]
    ,
    "name": "IEvaluable", "size": 335,
    "name": "IPredicate", "size": 383,
    "name": "IValueProxy", "size": 874,
    
     "name": "math",
     "children": [
      "name": "DenseMatrix", "size": 3165,
      "name": "IMatrix", "size": 2815,
      "name": "SparseMatrix", "size": 3366
     ]
    ,
    "name": "Maths", "size": 17705,
    "name": "Orientation", "size": 1486,
    
     "name": "palette",
     "children": [
      "name": "ColorPalette", "size": 6367,
      "name": "Palette", "size": 1229,
      "name": "ShapePalette", "size": 2059,
      "name": "SizePalette", "size": 2291
     ]
    ,
    "name": "Property", "size": 5559,
    "name": "Shapes", "size": 19118,
    "name": "Sort", "size": 6887,
    "name": "Stats", "size": 6557,
    "name": "Strings", "size": 22026
   ]
  ,
  
   "name": "vis",
   "children": [
    
     "name": "axis",
     "children": [
      "name": "Axes", "size": 1302,
      "name": "Axis", "size": 24593,
      "name": "AxisGridLine", "size": 652,
      "name": "AxisLabel", "size": 636,
      "name": "CartesianAxes", "size": 6703
     ]
    ,
    
     "name": "controls",
     "children": [
      "name": "AnchorControl", "size": 2138,
      "name": "ClickControl", "size": 3824,
      "name": "Control", "size": 1353,
      "name": "ControlList", "size": 4665,
      "name": "DragControl", "size": 2649,
      "name": "ExpandControl", "size": 2832,
      "name": "HoverControl", "size": 4896,
      "name": "IControl", "size": 763,
      "name": "PanZoomControl", "size": 5222,
      "name": "SelectionControl", "size": 7862,
      "name": "TooltipControl", "size": 8435
     ]
    ,
    
     "name": "data",
     "children": [
      "name": "Data", "size": 20544,
      "name": "DataList", "size": 19788,
      "name": "DataSprite", "size": 10349,
      "name": "EdgeSprite", "size": 3301,
      "name": "NodeSprite", "size": 19382,
      
       "name": "render",
       "children": [
        "name": "ArrowType", "size": 698,
        "name": "EdgeRenderer", "size": 5569,
        "name": "IRenderer", "size": 353,
        "name": "ShapeRenderer", "size": 2247
       ]
      ,
      "name": "ScaleBinding", "size": 11275,
      "name": "Tree", "size": 7147,
      "name": "TreeBuilder", "size": 9930
     ]
    ,
    
     "name": "events",
     "children": [
      "name": "DataEvent", "size": 2313,
      "name": "SelectionEvent", "size": 1880,
      "name": "TooltipEvent", "size": 1701,
      "name": "VisualizationEvent", "size": 1117
     ]
    ,
    
     "name": "legend",
     "children": [
      "name": "Legend", "size": 20859,
      "name": "LegendItem", "size": 4614,
      "name": "LegendRange", "size": 10530
     ]
    ,
    
     "name": "operator",
     "children": [
      
       "name": "distortion",
       "children": [
        "name": "BifocalDistortion", "size": 4461,
        "name": "Distortion", "size": 6314,
        "name": "FisheyeDistortion", "size": 3444
       ]
      ,
      
       "name": "encoder",
       "children": [
        "name": "ColorEncoder", "size": 3179,
        "name": "Encoder", "size": 4060,
        "name": "PropertyEncoder", "size": 4138,
        "name": "ShapeEncoder", "size": 1690,
        "name": "SizeEncoder", "size": 1830
       ]
      ,
      
       "name": "filter",
       "children": [
        "name": "FisheyeTreeFilter", "size": 5219,
        "name": "GraphDistanceFilter", "size": 3165,
        "name": "VisibilityFilter", "size": 3509
       ]
      ,
      "name": "IOperator", "size": 1286,
      
       "name": "label",
       "children": [
        "name": "Labeler", "size": 9956,
        "name": "RadialLabeler", "size": 3899,
        "name": "StackedAreaLabeler", "size": 3202
       ]
      ,
      
       "name": "layout",
       "children": [
        "name": "AxisLayout", "size": 6725,
        "name": "BundledEdgeRouter", "size": 3727,
        "name": "CircleLayout", "size": 9317,
        "name": "CirclePackingLayout", "size": 12003,
        "name": "DendrogramLayout", "size": 4853,
        "name": "ForceDirectedLayout", "size": 8411,
        "name": "IcicleTreeLayout", "size": 4864,
        "name": "IndentedTreeLayout", "size": 3174,
        "name": "Layout", "size": 7881,
        "name": "NodeLinkTreeLayout", "size": 12870,
        "name": "PieLayout", "size": 2728,
        "name": "RadialTreeLayout", "size": 12348,
        "name": "RandomLayout", "size": 870,
        "name": "StackedAreaLayout", "size": 9121,
        "name": "TreeMapLayout", "size": 9191
       ]
      ,
      "name": "Operator", "size": 2490,
      "name": "OperatorList", "size": 5248,
      "name": "OperatorSequence", "size": 4190,
      "name": "OperatorSwitch", "size": 2581,
      "name": "SortOperator", "size": 2023
     ]
    ,
    "name": "Visualization", "size": 16540
   ]
  
 ]
);


    </script>
  </body>
</html>

【问题讨论】:

【参考方案1】:

element.getBoundingClientRect() 方法将返回元素相对于视口的正确坐标,无论 svg 是否已被缩放和/或翻译。

See this question and answer。

虽然 getBBox() 适用于未转换的空间,但如果已将缩放和平移应用于布局,则它将不再准确。当平移和缩放生效时,getBoundingClientRect() 函数对我来说效果很好,我想将 HTML Div 元素作为标签附加到节点,而不是使用 SVG 文本元素。

【讨论】:

请注意,至少在 Firefox 59 中 element.getBoundingClientRect() 可以与 transform="rotate(25)" 一起使用,但不能与 style="transform: rotate(25deg)" 一起使用,例如CSS 转换。【参考方案2】:

确定坐标的方式取决于您使用的元素。以circles 为例,cxcy 属性确定中心位置。此外,您可以通过transform 属性应用translation,该属性会更改任何坐标的参考点。

通常用于获取屏幕坐标的大多数方法不适用于 SVG。此外,如果您要绘制的线与其连接的元素位于同一容器中,则您可能不需要绝对坐标。

编辑:

在您的特定代码中,很难获得节点的位置,因为它是由父元素的翻译决定的。所以你需要获取父节点的transform属性并从中提取翻译。

d3.transform(d3.select(this.parentNode).attr("transform")).translate

工作 jsfiddle here.

【讨论】:

我碰巧在使用圆圈,所以我将坐标更改为 .attr("x1", this.cx) 和 .attr("x1", this.cy) 但仍默认为零。如果圆的坐标是 cx 和 cy,它怎么能做到这一点?我完全误解了什么吗? (有可能,因为我还没有跳过 d3 的陡峭学习曲线:P) 完全取决于this 是什么。你试过d3.select(this).attr("cx")吗? this 是我正在使用的圈子。我也试过d3.select(this).attr("cx"),但仍然默认为零。 由于某种原因无法在 jsfiddle 上上传,所以在我的问题中包含了整个代码(应该很容易复制,因为我在代码中包含了 json)。我还强调了问题到底出在哪里。 如果需要的是相对于其容器的实际元素位置, element.getBoundingClientRect() 应该可以解决问题。见这里:***.com/questions/18554224/…【参考方案3】:

您可以使用函数 getBBox() 来获取路径的边界框。这将为您提供可能包含渲染路径的最紧凑矩形的位置和大小。

与读取xy 值相比,使用此方法的一个优点是它适用于所有图形对象。对象多于没有xy 的路径,例如有cxcy 的圆。

【讨论】:

【参考方案4】:

我使用合并功能,如下所示:

 element.transform.baseVal.consolidate()

.e 和 .f 值对应于 x 和 y 坐标

【讨论】:

这显然不再适用于 Chrome。它为我返回null【参考方案5】:

我可以这样处理;

svg.selectAll("rect")
    .data(zones)
    .enter()
    .append("rect")
    .attr("id", function (d)  return "zone" + d.zone; )
    .attr("class", "zone")
    .attr("x", function (d, i) 
        if (parseInt(i / (wcount)) % 2 == 0) 
            this.xcor = (i % wcount) * zoneW;
        
        else 
            this.xcor = (zoneW * (wcount - 1)) - ((i % wcount) * zoneW);
        

        return this.xcor;
    )

你可以找到x坐标

svg.select("#zone1").on("click",function()alert(this.xcor);

【讨论】:

【参考方案6】:
svg.selectAll("rect")
.attr('x',function(d,i)
    // get x coord
    console.log(this.getBBox().x, 'or', d3.select(this).attr('x'))
)
.attr('y',function(d,i)
    // get y coord
    console.log(this.getBBox().y)
)
.attr('dx',function(d,i)
    // get dx coord
    console.log(parseInt(d3.select(this).attr('dx')))
)

【讨论】:

谢谢,您对 getBBox() 的引用很有帮助,但一些解释可以完成答案【参考方案7】:

我试图用一个矩形选择一个 svg 区域并从中获取所有元素。为此, element.getBoundingClientRect() 非常适合我。它返回 svg 元素的当前坐标,无论 svg 是缩放还是变换。

【讨论】:

以上是关于如何获取 svg 元素的坐标?的主要内容,如果未能解决你的问题,请参考以下文章

如何获取相对于持有 onclick 监听器的 SVG 元素的点击坐标?

如何获取 svg:g 元素的宽度

在 svg 路径上获取点的 y 坐标

如何在具有特定宽度和高度的矩形中获取 svg 文本元素?

svg 实践之屏幕坐标与svg元素坐标转换

HTML5 - 如何获取 SVG 文本元素的中心点?