从div到div绘制弯曲的SVG箭头线

时间:2016-09-18 00:57:29

标签: javascript html css dom svg

我想使用SVG绘制两条弯曲的箭头线来连接两个元素以指示它们来回走动,如下所示:

enter image description here

我已经阅读了一些关于SVG的内容,但我并不完全确定如何创建一个垂直的线。

其次,如果SVG采用坐标,在创建SVG绘图之前是否必须找到元素的坐标位置?如果调整窗口大小,是否必须重新绘制?

3 个答案:

答案 0 :(得分:46)

创建svg元素(无形)作为整个文档的基础。这将保持两个箭头。插入两个svg path元素(箭头),其开始和结束坐标是根据要连接的div的位置计算的,并且根据这些元素的开始和方式以任何方式创建其曲线。结束坐标。

对于下面的示例,请点击"运行代码段"。然后单击并拖动div中的任何一个以查看箭头是如何动态创建的,即它们随div移动。 jQuery和jQueryUI在代码片段中使用,只是为了让div的易于拖动,与箭头的创建和使用无关。

这个例子有两个箭头,在div的中间开始和结束。两侧。当然,曲线的细节取决于您。箭头线是使用svg d的{​​{1}}属性构造的。在这个例子中," M"是" moveTo"坐标路径将开始的位置和" C"点是三次贝塞尔曲线的第一和第二控制点和最终坐标。您必须look those up才能了解它们是什么,但它们是在svg元素中创建平滑曲线的一般方法。使用svg path元素添加箭头,您可以阅读here

更复杂的文档需要更加谨慎地确定svg <marker>元素的开始和结束坐标,即箭头,但这个例子至少为您提供了一个开始的地方。

您特定问题的答案:

  • 如果SVG采用坐标,在创建SVG绘图之前是否必须找到元素的坐标位置?是的,正如我在我的代码中所做的那样。

  • 如果调整窗口大小,是否必须重新绘制?可能是的,取决于调整窗口大小时div本身会发生什么。

&#13;
&#13;
path
&#13;
var divA       = document.querySelector("#a");
var divB       = document.querySelector("#b");
var arrowLeft  = document.querySelector("#arrowLeft");
var arrowRight = document.querySelector("#arrowRight");

var drawConnector = function() {
  var posnALeft = {
    x: divA.offsetLeft - 8,
    y: divA.offsetTop  + divA.offsetHeight / 2
  };
  var posnARight = {
    x: divA.offsetLeft + divA.offsetWidth + 8,
    y: divA.offsetTop  + divA.offsetHeight / 2    
  };
  var posnBLeft = {
    x: divB.offsetLeft - 8,
    y: divB.offsetTop  + divA.offsetHeight / 2
  };
  var posnBRight = {
    x: divB.offsetLeft + divB.offsetWidth + 8,
    y: divB.offsetTop  + divA.offsetHeight / 2
  };
  var dStrLeft =
      "M" +
      (posnALeft.x      ) + "," + (posnALeft.y) + " " +
      "C" +
      (posnALeft.x - 100) + "," + (posnALeft.y) + " " +
      (posnBLeft.x - 100) + "," + (posnBLeft.y) + " " +
      (posnBLeft.x      ) + "," + (posnBLeft.y);
  arrowLeft.setAttribute("d", dStrLeft);
  var dStrRight =
      "M" +
      (posnBRight.x      ) + "," + (posnBRight.y) + " " +
      "C" +
      (posnBRight.x + 100) + "," + (posnBRight.y) + " " +
      (posnARight.x + 100) + "," + (posnARight.y) + " " +
      (posnARight.x      ) + "," + (posnARight.y);
  arrowRight.setAttribute("d", dStrRight);
};

$("#a, #b").draggable({
  drag: function(event, ui) {
    drawConnector();
  }
});

setTimeout(drawConnector, 250);
/* The setTimeout delay here is only required to prevent
 * the initial appearance of the arrows from being
 * incorrect due to the animated expansion of the
 * Stack Overflow code snippet results after clicking
 * "Run Code Snippet." If this was a simpler website,
 * a simple command, i.e. `drawConnector();` would suffice.
 */
&#13;
html,
body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}
#instructions {
  position: fixed;
  left: 50%;
}
#a, #b {
  color: white;
  text-align: center;
  padding: 10px;
  position: fixed;
  width: 100px;
  height: 20px;
  left: 100px;
}
#a {
  background-color: blue;
  top: 20px;
}
#b {
  background-color: red;
  top: 150px;
}
&#13;
&#13;
&#13;

答案 1 :(得分:3)

我发现安德鲁·威廉姆斯的答案非常有用。我已经对其进行了修改,以创建一个库draw_arrow.js,该库导出了一个函数draw_arrow( sel1, locs1, sel2, locs2, arr )。这从CSS选择器sel1标识的元素到sel2标识的元素绘制了一个箭头。 locs1locs2指示箭头应在元素上的开始或结束位置。 arr标识要保留箭头的SVG路径。

您可以从http://www.chromophilia.uk/blog/dress-reform-architecture-and-modernism/末尾的链接下载此文件并查看两个演示。作为动画的一部分,我需要箭头来描述与现代主义相关的各个主题之间的关系。这就是促使我找到并改编安德鲁代码的原因。

这是建议的改进。我最初将其写为一个新的附加答案,但是一些评论者已经执行了该命令,因此我必须将其放在此处,并希望它能引起注意。我之所以这样做,是因为模块化很重要。诸如draw_arrow之类的例程应要求其用户对周围的代码进行尽可能少的处理。但是目前,它需要用户在<path>内为要绘制的每个箭头创建一个<svg>元素,并为路径创建ID。我建议通过更新DOM树来使draw_arrow更好。赞成还是反对评论?

答案 2 :(得分:3)

我们终于有了它!看看这个:

https://www.npmjs.com/package/arrows-svg

还有一个React版本:

https://www.npmjs.com/package/react-arrows

因此,如果您有两个div,根据您示例中的div分别将其ID分别命名为fromto,那么您可以这样做:

import arrowCreate, { DIRECTION } from 'arrows'

const arrow = arrowCreate({
  className: 'arrow',
  from: {
    direction: DIRECTION.LEFT,
    node: document.getElementById('from'),
    translation: [-0.5, -1],
  },
  to: {
    direction: DIRECTION.LEFT,
    node: document.getElementById('to'),
    translation: [0.9, 1],
  },
})

/*
  - arrow.node is HTMLElement
  - arrow.timer is idInterval from setInterval()
    REMEMBER about clearInterval(node.timer) after unmount
*/
document.body.appendChild(arrow.node);

当然还有一些CSS:

.arrow {
  pointer-events: none;
}

.arrow__path {
  stroke: #000;
  fill: transparent;
  stroke-dasharray: 4 2;
}

.arrow__head line {
  stroke: #000;
  stroke-width: 1px;        
}

经过测试,可以正常工作!