【问题标题】:Draw line between rectangle from border to border从边框到边框在矩形之间画线
【发布时间】:2021-05-24 02:29:17
【问题描述】:

我想画一条线将两个矩形从边框连接到边框并穿过两个矩形中心。目前,我可以从中心到中心画线。我知道如果我用白色填充矩形,那么线条连接看起来就像从边框到边框。但我确实希望保持矩形透明!

实现它的正确方法是什么?

var svg = d3.select('body').append('svg')
.attr('width',800)
.attr('height',600)
.style('border','1px solid red')
.style('background-color','#e0e0e0')

 svg.append("defs")
    .append("marker")
    .attr("id","pointer")
    .attr("markerWidth", 10)
    .attr("markerHeight", 10)
    .attr("orient","auto")
    .attr("refY", 5)
    .append("path")
    .attr("d", "M 0 0 L 10 5 L 0 10 z")
    

var data = [
  {
    id:0,
    x:100,
    y:100,
    width:100,
    height:50,
  },{
    id:1,
    x:200,
    y:200,
    width:100,
    height:50,
  }
]

var links = [
  {
    src:0,
    dest:1,
  }
]

svg.selectAll(null)
.data(data)
.enter()
.append('rect')
.attr('x', d => d.x - d.width/2)
.attr('y', d => d.y - d.height/2)
.attr('width',d => d.width)
.attr('height', d => d.height)
.attr('fill','none')
.attr('stroke','black')
.attr('stroke-width',2)

svg.selectAll(null)
.data(links)
.enter()
.append('line')
.attr('x1',d => data.find(e => e.id === d.src).x)
.attr('y1',d => data.find(e => e.id === d.src).y)
.attr('x2',d => data.find(e => e.id === d.dest).x)
.attr('y2',d => data.find(e => e.id === d.dest).y)
.attr('stroke','black')
.attr("marker-end","url(#pointer)");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

【问题讨论】:

  • 你可以从古老的“上升超过运行”方程中得到直线的斜率。您还知道矩形的大小及其中心的位置。中心被“掩埋”的量控制着到达矩形边框需要移动多少线,如果矩形是 2 个单位宽,你需要移动 up-to i> X 方向 1 个单位。因此,运行常数上升的 1 倍告诉您修改 Y 坐标的程度。想想从正方形中心的 45 度线......每个坐标的变化与另一个相同。矩形只是比另一个更快地改变一个。

标签: javascript svg d3.js


【解决方案1】:

使用linkPath函数:

const first = {x: 50, y: 50, width: 40, height: 30};
const second = {x: 200, y: 110, width: 70, height: 40};
const third = {x: 80, y: 120, width: 50, height: 20};

const linkPath = (from, to) => {
  const dx = to.x - from.x;
  const dy = to.y - from.y;
  let fX, fY, tX, tY;
  const factor = dx > 0 ^ dy > 0;
  
  if (from.width / from.height > Math.abs(dx / dy)) {
    fX = from.x + from.height * dx / dy / 2 * (factor ? -1 : 1);
    fY = from.y + from.height / 2 * (factor ? -1: 1);
  }
  else {
    fX = from.x + from.width / 2 * (factor ? -1 : 1);
    fY = from.y + from.width * dy / dx / 2 * (factor ? -1 : 1);
  }

  if (to.width / to.height > Math.abs(dx / dy)) {
    tX = to.x + to.height * dx / dy / 2 * (factor ? 1 : -1);
    tY = to.y + to.height / 2 * (factor ? 1 : -1);
  }
  else {
    tX = to.x + to.width / 2 * (factor ? 1 : -1);
    tY = to.y + to.width * dy / dx / 2 * (factor ? 1 : -1);
  }

  return {from: {x: fX, y: fY}, to: {x: tX, y: tY}};  
};


const svg = d3.select('svg');

const drawRectangle = rect => 
  svg.append('rect')
    .attr('x', rect.x - rect.width / 2) 
    .attr('y', rect.y - rect.height / 2) 
    .attr('width', rect.width) 
    .attr('height', rect.height);
    
const drawLink = (from, to) => {
    const path = linkPath(from, to);
  svg.append('line')
    .attr('x1', path.from.x)
    .attr('y1', path.from.y)
    .attr('x2', path.to.x)
    .attr('y2', path.to.y)
    .attr('marker-end', 'url(#arrowhead)')
} 
    
    
drawRectangle(first);
drawRectangle(second);
drawRectangle(third);
drawLink(first, second);
drawLink(first, third);
drawLink(second, third);
rect {
  fill: none;
  stroke: black;
}

line {
  stroke: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="300" height="200">
  <defs>
    <marker id="arrowhead" markerWidth="10" markerHeight="7" 
    refX="10" refY="3.5" orient="auto">
      <polygon fill="black" points="0 0, 10 3.5, 0 7" />
    </marker>
  </defs>
</svg>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 1970-01-01
    • 1970-01-01
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多