Skip to content

생키 ​

renderSankey는 노드 사이의 흐름을 값에 비례하는 폭의 리본으로 그리고, 노드를 열로 배치합니다. 예산, 에너지 수지, 사용자 여정처럼 방향이 있고 순환이 없는 흐름에 씁니다.

라이브 예제

기본 사용법 ​

ts
import { Charturbo } from 'charturbo';

const canvas = document.querySelector('canvas')!;
const sdk = await Charturbo.create(canvas);

const link = (source: string, target: string, value: number) => ({ source, target, value });
sdk.renderSankey(
  {
    nodes: ['석탄', '가스', '태양광', '전력', '열', '가정', '산업'].map((id) => ({ id })),
    links: [
      link('석탄', '전력', 30), link('석탄', '열', 12),
      link('가스', '전력', 22), link('가스', '열', 18),
      link('태양광', '전력', 14),
      link('전력', '가정', 36), link('전력', '산업', 30),
      link('열', '가정', 18), link('열', '산업', 12),
    ],
  },
  { title: '에너지 (TWh)', format: ',.0f' }
);
sdk.enableNodeInteractions(canvas, { autoRender: true });
sdk.render();

시그니처:

ts
renderSankey(data: SankeyChartData, options?: SankeyChartOptions): { nodeCount: number; linkCount: number }

데이터 형식 ​

ts
interface SankeyChartData {
  nodes: SankeyNodeInput[];
  links: SankeyLinkInput[];
}

interface SankeyNodeInput {
  id: string | number;
  name?: string;        // 표시 이름 (기본: id)
  color?: ColorInput;
  group?: string;       // 색 그룹 (처음 나온 순서로 팔레트 배정; 범례에 그룹을 나열)
  column?: number;      // 최소 열 (0 = 왼쪽)
}

interface SankeyLinkInput {
  source: string | number; // 노드 id
  target: string | number; // 노드 id
  value: number;           // 유한하고 0 이상
}
  • 노드 값은 유입과 유출 중 큰 값입니다.
  • column은 최솟값입니다. 노드는 항상 모든 출발 노드보다 오른쪽에 놓이고, 사용된 열만 남깁니다.
  • 다음 입력은 예외를 던집니다.
    • 중복된 노드 id, 또는 알 수 없는 id를 가리키는 링크(Sankey link 3 has an unknown source "X")
    • 음수이거나 유한하지 않은 링크 값
    • 자기 자신으로 가는 링크, 또는 순환을 이루는 링크(Sankey links form a cycle: "A" -> "B" -> "A")
    • 양 끝 노드에 모두 column을 명시했는데 같은 열이나 앞쪽 열로 가는 링크

옵션 ​

옵션타입기본값설명
nodeWidthnumber12노드 막대 폭(CSS px, 세로 방향이면 막대 높이).
nodePaddingnumber10같은 열 노드 사이의 간격(CSS px).
orient'horizontal' | 'vertical''horizontal'흐름 방향: 왼쪽에서 오른쪽으로 가는 열, 또는 위에서 아래로 가는 행. 세로 레이아웃 참고.
iterationsnumber32레이아웃 이완 반복 횟수.
align'justify' | 'left' | 'right' | 'center''justify'column이 없는 노드의 열 배정. 'justify'는 도착 노드(sink)를 마지막 열에 놓습니다.
linkColor'source' | 'target' | 'neutral' | 'gradient''source'리본 색. 'gradient'는 출발 색에서 도착 색으로 섞습니다.
linkOpacitynumber0.5리본 불투명도.
colorsColorInput[]테마 팔레트고정 순서의 범주형 팔레트.
levelsSankeyLevelOptions[]—열별 노드 색과 링크 불투명도. 색과 라벨 참고.
labelsbooleantrue막대 옆 노드 라벨.
formatChartValueFormat로케일 숫자라벨, 툴팁, 접근성의 값 형식. 숫자·날짜 형식 참고.
valueLabelstring'값' (로캘별)툴팁과 데이터 표에서 값의 이름.
titlestring차트 제목.
descriptionstring캔버스의 접근성 이름.
legendChartLegendOptions | boolean그룹 2개 이상이면 표시노드 그룹을 나열합니다. 정적이라 토글이나 강조가 없습니다.
paddingChartPaddingOptions자동플롯 여백(CSS px).

레이아웃 ​

레이아웃은 d3-sankey 방식을 따르며 엔진(Rust)에서 계산합니다.

  1. 열: 출발 노드로부터의 최장 경로를 기준으로 하고, align이나 명시적 column으로 조정합니다.
  2. 노드 높이는 값에 비례하고, 가장 빽빽한 열이 들어가도록 배율을 정합니다.
  3. iterations회 이완: 노드를 이웃의 가중 위치 쪽으로 옮기고(오른쪽 → 왼쪽, 왼쪽 → 오른쪽), 열마다 충돌을 해소합니다. 마지막 회차 이후 같은 열의 노드는 겹치지 않습니다.

링크는 양 끝에서 수평 접선을 갖는 3차 베지어 리본입니다. 반투명으로, 두꺼운 것부터 그려서 얇은 흐름이 위에 보입니다.

세로 레이아웃 ​

ts
sdk.renderSankey(data, {
  title: '에너지 (TWh)',
  orient: 'vertical',
  linkColor: 'gradient',
  levels: [
    { column: 0, linkOpacity: 0.65 },
    { column: 2, color: '#64748b' },
  ],
});
  • orient: 'vertical'은 같은 레이아웃을 흐름 좌표(흐름 방향과 가로지르는 방향)에서 계산하고 화면에서 x와 y를 바꿉니다. 열은 위에서 아래로 쌓이는 행이 됩니다.
  • hit test, 키보드 위치, 내보내는 노드 사각형도 같은 변환을 씁니다.
  • 라벨은 막대 가운데에 맞춥니다. 위쪽 절반의 노드는 막대 아래에, 나머지는 위에 놓습니다. 같은 행에서 라벨 중심이 가장 긴 라벨 폭(SDK 추정)보다 가까우면 라벨을 빼고, 큰 노드의 라벨을 남깁니다.

라이브 예제

색과 라벨 ​

  • 노드 색: group이 있으면 그룹별, 아니면 노드 수가 팔레트 이하일 때 노드별, 그 밖에는 열별입니다(팔레트를 넘는 노드는 회색).
  • 라벨은 이름과 값을 표시합니다. 왼쪽 절반의 노드는 오른쪽에, 나머지는 왼쪽에 놓습니다. 같은 열에서 두 라벨이 줄 간격보다 가까우면 큰 노드의 라벨을 남깁니다.
  • linkColor: 'gradient': 각 리본의 색이 리본을 따라 출발 노드 색에서 도착 노드 색으로 선형으로 바뀝니다.
  • levels(ECharts levels와 같음)는 열의 스타일을 정합니다: 노드 색(노드 자신의 color가 우선)과 그 열에서 나가는 링크의 불투명도. 같은 열을 여러 항목이 지정하면 마지막 항목이 이깁니다.
levels[] 필드타입기본값설명
columnnumber—열 인덱스(0 = 출발 노드). 쓰이지 않는 열을 뺀 뒤의 번호입니다.
colorColorInput위의 노드 색 규칙그 열 노드의 색.
linkOpacitynumber차트 linkOpacity그 열에서 나가는 링크의 불투명도, 0~1.

상호작용 ​

enableNodeInteractions(canvas)를 호출하면 켜집니다. hover는 노드를 먼저 찾고, 그다음 리본을 위에서부터 찾습니다. hover한 노드나 링크에 연결된 흐름만 밝게 표시합니다. 드릴다운은 없습니다. 브러시, 줌, 커서 동기화는 비활성입니다.

툴팁 컨텍스트(툴팁과 hover 참고):

ts
// ctx.chartType === 'sankey'
// ctx.flow = { kind: 'link', source, target, value, percentOfSource }
//          | { kind: 'node', source, value, inflow, outflow }   // 노드: `source`에 노드 이름

데이터 포인트 id: 노드는 노드 인덱스, 링크는 노드 수 + 링크 인덱스.

참고 사항 ​

  • 이완 1회 비용은 O(N + L log L)이라 허브 노드가 있어도 부담이 적습니다.
  • 접근성: 요약(노드 · 열 · 링크 수, 가장 큰 흐름), 데이터 표(링크마다 출발, 도착, 값, 출발 대비 %), 좌우 화살표로 링크 이동. 접근성 참고.

함께 보기 ​

MIT License