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

Posted

技术标签:

【中文标题】从 div 到 div 绘制弯曲的 SVG 箭头线【英文标题】:Drawing curved SVG arrow lines from div to div 【发布时间】:2017-01-25 23:00:43 【问题描述】:

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

我读过一些关于 SVG 的文章,但我不完全确定如何创建一条垂直的线。

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

【问题讨论】:

目前尚不清楚这在不同情况下应该如何表现。在最简单的层面上,如果高度是动态的,您将获得一个基本的弯曲箭头图像...... 【参考方案1】:

制作一个svg 元素,它(不可见)位于整个文档的基础。这将持有两个箭头。插入两个 svg path 元素(箭头),其开始和结束坐标是根据要连接的 div 的位置计算的,并且其曲线是根据这些开始和结束坐标以您想要的任何方式创建的。

对于下面的示例,单击“运行代码 sn-p”。然后单击并拖动其中一个 div 以查看箭头是如何动态创建的,即它们与 div 一起移动。 jQuery和jQueryUI在sn-p代码中的使用只是为了方便div的拖拽,与箭头的创建和使用无关。

这个例子有两个箭头,从 div 两侧的中间开始和结束。当然,曲线的细节取决于您。箭头线是使用 svg pathd 属性构造的。在此示例中,“M”是路径开始的“moveTo”坐标,“C”点是三次贝塞尔曲线的第一个和第二个控制点以及最终坐标。您必须look those up 才能了解它们是什么,但它们是在 svg 元素中创建平滑曲线的一般方法。箭头是使用 svg <marker> 元素添加的,您可以阅读有关 here 的信息。

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

回答您的具体问题:

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

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

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  + divB.offsetHeight / 2
  ;
  var posnBRight = 
    x: divB.offsetLeft + divB.offsetWidth + 8,
    y: divB.offsetTop  + divB.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.
 */
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;
<p id="instructions">Click and drag either div to see automatic arrow adjustments.</p>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
<svg xmlns="http://www.w3.org/2000/svg"  >
  <defs>
    <marker id="arrowhead" viewBox="0 0 10 10" refX="3" refY="5"
        markerWidth="6" markerHeight="6" orient="auto">
      <path d="M 0 0 L 10 5 L 0 10 z" />
    </marker>
  </defs>
  <g fill="none" stroke="black" stroke- marker-end="url(#arrowhead)">
    <path id="arrowLeft"/>
    <path id="arrowRight"/>
  </g>
</svg>
<div id="a">Div 1</div>
<div id="b">Div 2</div>

【讨论】:

多么好的答案!我会在手机上设置一个提醒来奖励赏金 我想你忘了奖励赏金了。 非常感谢您用«Guru» 银牌奖励您的回答!这是非常好的和有用的答案! 在全页模式下你有一个bug:在sn-p执行后立即切换到全页,你会看到this。要更正它,您可以将#b bottom: 20px; 更改为#b top: 180px; 使用here【参考方案2】:

我发现 Andrew Willems 的回答非常有用。我已经修改它以创建一个库 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/ 末尾的链接中下载并查看两个演示。作为动画的一部分,我需要箭头来描绘与现代主义相关的各种主题之间的关系。这就是驱使我找到并修改 Andrew 的代码的原因。 注意:该链接目前无法正常工作,因为 WordPress 数据库存在一些问题,我必须修复它。箭头库和用于连续显示 HTML 元素的库以及演示,可以通过下面我对 Henry Mont 的评论中的链接获得。

这是一个改进建议。我最初把它写成一个新的、附加的答案,但有几位评论者对此表示不满,所以我不得不把它放在这里,希望它能引起注意。我正在追求这一点,因为模块化很重要。诸如draw_arrow 之类的例程应该要求其用户尽可能少地对其周围的代码执行操作。但目前,它需要用户在&lt;svg&gt; 内为每个要绘制的箭头创建一个&lt;path&gt; 元素,并为路径创建ID。我建议draw_arrow 通过更新 DOM 树来执行此操作会更好。赞成还是反对?

【讨论】:

很高兴看到您发现我的回答很有用,并且您可以根据自己的工作进行调整。我喜欢你的动画。继续努力。 嘿,我知道我来晚了,但是您提供的链接似乎已失效,是否仍然可以在某个地方下载您的库? @Henry Mont chromophilia.uk/web_techniques/animate_progression.js,chromophilia.uk/web_techniques/draw_arrow.js,chromophilia.uk/web_techniques/draw_arrow_demo.html。前两个是库,后两个是演示。我现在无法理解现代主义的例子:似乎有什么东西破坏了 WordPress 使用的数据库。但是您已经在这些链接中获得了所有基本代码。希望对您有所帮助。 @PhilvanKleur 谢谢,它可能会有很大帮助:) @Henry Mont 太棒了!对不起这个问题。请注意我回答中的最后一段。您也许可以使代码更加模块化,如果您要使用我的代码构建许多不同的页面,或者经常更改它,这可能会有所帮助。【参考方案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;        

经过测试,它可以工作!

【讨论】:

以上是关于从 div 到 div 绘制弯曲的 SVG 箭头线的主要内容,如果未能解决你的问题,请参考以下文章

带有vue和svg的弯曲底部边框[重复]

CSS 3D 将 div 转换为弯曲的拱形

用 svg 线连接 div

matlab 有关figure图 中箭头怎么画

尝试将 svg 从一个 div 移动到另一个 div 时导致层次结构请求错误的原因是啥?

如何用svg在网页中画一条带箭头的连接线