D3ForceLayout: 配置的 center / centerX / x / y / radial 坐标及 radius 参数被默认值覆盖失效

オープン 初心者向け
#294 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

@chilingling がすでに取り組んでいます。

2026年10月9日 から。

  • #295 @chilingling による — オープン

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
75/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
d3, typescript

調査の方向性

マージ順序の問題は src/algorithm/d3-force/index.ts の getCenterOptions、getXForceOptions、getYForceOptions、getRadialOptions にあり、ユーザーオプションの後に assignDefined を通じてデフォルト値が展開されています。まずissueの再現スクリプトを実行し、重心が [350, 350] であること、radial.radius の値が 100 であることを確認してください。ユーザーが指定した center、centerX/centerY、force の x/y/radial 座標、radial.radius が反映され、未設定のレイアウトが変わらなければ完了です。

索引モデルが issue の本文から書いたものです。

説明

版本
  • @antv/layout: 2.0.1(npm beta),v5 分支(62bb479)同样可复现
  • Node.js: 24
问题描述

D3ForceLayout 在合并配置时,默认值后置覆盖了用户传入的参数,导致以下自定义配置失效:

  • 中心点配置失效:center: { x, y } 与文档推荐的快捷字段 centerX / centerY 均不生效,中心点始终固定为视口中心 [width / 2, height / 2](且源码中未读取 centerX / centerY)。
  • 力的坐标被覆盖:中心力开启时(默认开启),x: { x }、y: { y }、radial: { x, y } 均会被中心点坐标覆盖。
  • 径向半径被覆盖:radial: { radius } 始终被默认值 100 覆盖。
复现
const { D3ForceLayout } = require('@antv/layout');

const data = {
  nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }, { id: 'd' }],
  edges: [
    { id: 'e1', source: 'a', target: 'b' },
    { id: 'e2', source: 'b', target: 'c' },
    { id: 'e3', source: 'c', target: 'd' },
  ],
};

const centroid = async (options) => {
  const layout = new D3ForceLayout({ width: 700, height: 700, ...options });
  await layout.execute(data);
  let x = 0, y = 0, n = 0;
  layout.forEachNode((node) => { x += node.x; y += node.y; n += 1; });
  return [Math.round(x / n), Math.round(y / n)];
};

(async () => {
  console.log(await centroid({ center: { x: 300, y: 200 } })); // 实际: [350, 350], 期望: [300, 200]
  console.log(await centroid({ centerX: 300, centerY: 200 })); // 实际: [350, 350], 期望: [300, 200]

  const layout = new D3ForceLayout({
    width: 700,
    height: 700,
    x: { x: 100 },
    radial: { x: 100, y: 100, radius: 50 },
  });
  await layout.execute(data);
  console.log(layout.getForce('x').x()()); // 实际: 350, 期望: 100
  console.log(layout.getForce('radial').x(), layout.getForce('radial').y()); // 实际: 350 350, 期望: 100 100
  console.log(layout.getForce('radial').radius()()); // 实际: 100, 期望: 50
})();
问题原因

在 src/algorithm/d3-force/index.ts 中,getCenterOptions、getXForceOptions、getYForceOptions 和 getRadialOptions 合并配置时使用了如下写法:

return assignDefined({}, options.center || {}, {
  x: viewport.width / 2,
  y: viewport.height / 2,
  strength: options.centerStrength,
});

默认值对象放在了用户配置对象之后,直接覆盖了用户传入的自定义值(如视口中心 width / 2、中心点坐标、radius: 100)。

修复建议

将默认值前置,调整合并顺序为:默认值 → force 对象 → 快捷字段。
这样仅在未配置时使用默认值,未配置相关参数的已有布局不受影响。我稍后会提交对应的修复 PR。

待确认问题
  1. 配置优先级:文档 d3-force-options 说明为“两者混用时以 force 对象为准”,但当前代码实现中所有 getter(如 manyBody 与 nodeStrength、center.strength 与 centerStrength)均为“快捷字段优先覆盖 force 对象”。PR 沿用了现有实现的约定,是否需要后续统一调整文档或实现?
  2. 中心配置文档说明:同一文档的“通用配置”中提到“D3ForceLayout 中心相关配置不使用 center,而是使用 centerX/centerY”,但在“2D 扩展”章节和 TypeScript 类型定义中均支持 center: false | { x?, y?, strength? }。该处文档说明是否需要同步修正?
主要言語
TypeScript
スター
280
フォーク
61
PR マージ指標
30日以内にマージされた PR はありません

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

antvis/layout のほかの issue

antvis/layout の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。