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

dsp*_*099 15 html javascript css svg dom

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

在此输入图像描述

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

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

And*_*ems 59

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

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

这个例子有两个箭头开始和结束在div的两侧.当然,曲线的细节取决于您.箭头线是使用dsvg 的属性构造的path.在这个例子中,"M"是路径将开始的"moveTo"坐标,"C"点是立方贝塞尔曲线的第一和第二控制点和最终坐标.你必须看看那些高达了解它们是什么,但他们在SVG元素创建平滑曲线的一般方式.箭头是使用svg <marker>元素添加的,您可以在这里阅读.

更复杂的文档需要更加谨慎来确定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  + 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.
 */
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<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" width="100%" height="100%">
  <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-width="2" marker-end="url(#arrowhead)">
    <path id="arrowLeft"/>
    <path id="arrowRight"/>
  </g>
</svg>
<div id="a">Div 1</div>
<div id="b">Div 2</div>
Run Code Online (Sandbox Code Playgroud)

  • 多么好的答案!我会在手机上设置提醒以奖励赏金 (5认同)
  • 我想你忘了奖励赏金. (4认同)
  • 我非常感激地用“Guru”银徽章奖励您的回答!这是非常好的和有用的答案! (2认同)
  • 在完整页面模式中,您有错误:在片段执行后立即更改为完整页面,您将看到**[this](https://i.stack.imgur.com/2hPs7.png)**.要纠正它,您可以将`#b {bottom:20px;}`更改为`#b {top:180px;}`. (2认同)
  • [此处]使用(https://webglfundamentals.org/webgl/lessons/resources/webgl-state-diagram.html) (2认同)

Phi*_*eur 6

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

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

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

  • 很高兴看到您发现我的回答很有用,并且可以将其修改为自己的工作。我喜欢你的动画。保持良好的工作。 (2认同)

tur*_*kus 6

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

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

还有一个 React 版本:

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

因此,如果您有两个 div,让我们说 ids named:fromto根据您示例中的 div,那么您可以:

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);
Run Code Online (Sandbox Code Playgroud)

当然还有一些css:

.arrow {
  pointer-events: none;
}

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

.arrow__head line {
  stroke: #000;
  stroke-width: 1px;        
}
Run Code Online (Sandbox Code Playgroud)

经测试有效!