Day28 D3js Diagram常见的两点浪漫路径

D3js Diagram常见的两点浪漫路径

用途

在绘制diagram图表时,会用到的垂直水平连线,并且在特定位置折角,虽然d3提供了很多绘制Bezier的方法,可是实际上除了数据分析或特别的图表外,我很少遇到需要Bezier的图表。

概念

其实流程很简单,几个步骤而已。

  1. 绘制两个方块
  2. 连线两个方块
  3. 拖拉事件时更新线段路径

拖拉事件

范例:

rect2 = rootLayer
    .append("rect")
    .attr("width", 100)
    .attr("height", 100)
    .attr("x", 500)
    .attr("y", 50)
    .call(
      d3
        .drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended)
    );

以上范例即是放置一个rect,并设定属性後,使用d3.drag()绑定拖拉事件。

Callback的部分:

function dragstarted(event) {}

function dragged(event) {
  let target = d3.select(this);
  let offsetX = event.x - target.attr("x");
  let offsetY = event.y - target.attr("y");
  target
    .attr("x", event.x - offsetX + event.dx)
    .attr("y", event.y - offsetY + event.dy);
}
function dragended(event) {}

我们透过d3.select(this)取得拖拉事件并产生Selection
读取目前点击的位置与目标被点击元件的位置算出offset後,更新目标被点击元件的位置并加上位移值,为何要算出offset仅是因为不想让拖拉事件一定都会以左上角定位。

绘制线段

最近路线版本

const genLine = (source, target) => {
  let ctx = d3.path();
  ctx.moveTo(source.x, source.y);
  ctx.lineTo(target.x, target.y);
  return ctx.toString();
};

其实就是直接将目前位置跟目标位置直接连线。

折线版本

const genLine = (source, target) => {
  let ctx = d3.path();
  let dx = Math.abs(source.x - target.x);
  let dy = Math.abs(source.y - target.y);

  // 两点距离
  let dl = Math.sqrt( dx * dx + dy * dy );
  
  // 两点XY距离加总
  let tl = Math.abs(dx + dy);
  
  // 中点座标
  let mp = {
    x: ((source.x + target.x) / 2),
    y: ((source.y + target.y) / 2)
  }
  
  // 如果小於150,没必要特别绕中点
  if (tl - dl <= 150) {
    // 连线垂直即可
    ctx.moveTo(source.x, source.y);
    ctx.lineTo(source.x, target.y);
    ctx.lineTo(target.x, target.y);
  } else {
    // 先连线至中点
    ctx.moveTo(source.x, source.y);
    ctx.lineTo(mp.x, source.y);
    // 再连线至终点
    ctx.lineTo(mp.x, target.y);
    ctx.lineTo(target.x, target.y);
  }

  return ctx.toString();
};

其实以最後一部分来看,也只是先到达目标位置的Y,再到达目标位置的X,至於为何会有分先连线至中点呢?

中点折线

产生结过为:

普通折线

如果两点直线连线以及折线长度相差未超过150,就会只采用普通折现,不会到中点。

结果为下:

结论

其实很多线图,应该有更复杂更多的判断,像是绕开碰撞元件,线段起始方向,等等更多常见diagram会实现的功能。

d3已经让我们非常方便操作线段了,只要自己写些数学公式,可能配合三角函数,可以玩出更多花样。

Codepen范例

参考

d3-api


<<:  [ Day:29 ] GitHub Actions 懒人部署 - 如何安装多个来源的 npm package

>>:  Day28练习java-多执行序

什麽是 HTTP ?

前言 该文章同步发布於 我的部落格 有天在网路上看到一张很有趣的图片,是关於浏览器输入网址後发生的行...

第八天:安装 IntelliJ IDEA

为了在後续章节里示范 TeamCity 可以怎麽协助我们建置专案及一系列的自动化,我们需要有一个可以...

Day9 - 字元及字串 (今天有请别的讲师来讲话喔w)

大家好,我是长风青云。今天是铁人赛的第九天。 不说那麽多了,先上片。 惊不惊喜?意不意外?这是我在公...

17.unity显示/隐藏物件(SetActive)

想要制作一个假背包,利用按钮显示背包,再按下按钮关闭背包。 要使用GameObject.SetAct...

[Day12] Storybook - Writing Docs

DocsPage DocsPage 是由 Storybook Docs 所提供的页面,无需任何的设定...