---
code: "TIM-12"
id: "c-horizon"
tab: "timeline"
kind: "core"
title: "Horizon Chart · 호라이즌"
library: "ECharts"
deps: ["echarts@5"]
load: "low"
pasteKind: "echarts-mount"
family: "distribution"
dataShape: "value distribution or density bins"
tasks: ["show trend", "show seasonality", "show sequence", "show spread"]
tags: ["time-series", "trend", "timeline", "distribution", "density", "horizon", "chart", "호라이즌"]
gallery: "https://graph.heal7.com/#TIM-12"
js: "/sku/TIM-12.js"
---

# [TIM-12] Horizon Chart · 호라이즌

> AI 붙여넣기 카드. 갤러리 런타임(`main.js`)이 아니라 이 파일을 가져와라.

## 언제 가져가나
좁은 띠 안에서 양/음을 색 레이어로 압축 — 좁은 공간에 큰 변동 폭을 담는다.

**응용** 다중 종목 가격 패널, 다중 센서 비교, 부서별 KPI 한 화면.

## 데이터 모양
- family: `distribution`
- dataShape: value distribution or density bins
- tasks: show trend, show seasonality, show sequence, show spread
- tags: time-series, trend, timeline, distribution, density, horizon, chart, 호라이즌


## 의존
echarts@5 · load `low` · library `ECharts`

## 붙여넣는 법
1. echarts 5.x 로드 (GL이면 echarts-gl도)
2. 컨테이너 id를 원본 id와 맞추거나 `document.getElementById` 한 줄만 바꾼다
3. `T`, `reg`, `cssv` 가 필요하면 갤러리 테마 스텁을 함께 가져간다

```js
const T = {
  "bg": "transparent",
  "fg": "#ece8f5",
  "sub": "#a59bbd",
  "grid": "rgba(255,255,255,.08)",
  "gold": "#e8c069",
  "royal": "#6b8cff",
  "rose": "#e07a9f",
  "teal": "#6dd2c4",
  "plum": "#b487e8"
};
const cssv = (n, fb) => (getComputedStyle(document.documentElement).getPropertyValue(n).trim() || fb);
const isDark = () => document.documentElement.getAttribute('data-theme') !== 'light';
const charts = [];
const reg = (id, opt) => {
  const dom = document.getElementById(id); if (!dom) return;
  const c = echarts.init(dom, null, {renderer:'canvas'});
  c.setOption(opt);
  charts.push(c);
  return c;
};
const factory = function(){
  const N = 80, ROWS = 4;
  const series = [];
  const names = ['CPU','MEM','NET','I/O'];
  const palette = [T.rose, T.gold, T.royal, T.teal];
  for(let r=0;r<ROWS;r++){
    const arr=[]; let v=0;
    for(let i=0;i<N;i++){ v = Math.sin(i/8 + r) * 30 + (Math.random()*20-10); arr.push([i, v]); }
    series.push({name:names[r], data:arr, color:palette[r]});
  }
  const opt = {
    backgroundColor:T.bg,
    tooltip:{trigger:'axis'},
    legend:{data:names, top:0, textStyle:{color:T.sub}},
    grid: series.map((_,i)=>({left:60, right:20, top: 24 + i*52, height: 44})),
    xAxis: series.map((_,i)=>({gridIndex:i, type:'value', show: i===series.length-1, axisLine:{lineStyle:{color:T.grid}}, axisLabel:{color:T.sub, fontSize:9}, max:N-1, min:0})),
    yAxis: series.map((s,i)=>({gridIndex:i, type:'value', min:-50, max:50, splitNumber:1, axisLine:{show:false}, axisTick:{show:false}, splitLine:{show:false}, axisLabel:{show:false}, name:s.name, nameLocation:'middle', nameGap:36, nameTextStyle:{color:T.sub, fontSize:11}})),
    series: series.flatMap((s,i)=>[
      {type:'line', name:s.name, xAxisIndex:i, yAxisIndex:i, data:s.data.map(d=>[d[0], Math.max(0,d[1])]), areaStyle:{color:s.color, opacity:.85}, lineStyle:{width:0}, symbol:'none', smooth:true},
      {type:'line', xAxisIndex:i, yAxisIndex:i, data:s.data.map(d=>[d[0], Math.min(0,d[1])]), areaStyle:{color:s.color, opacity:.4}, lineStyle:{width:0}, symbol:'none', smooth:true}
    ])
  };
  reg('c-horizon', opt);
};
factory();
```

## 원본 factory (갤러리 정본 발췌)
- file: `frontend/js/graph-gallery/main.js`
- lines: 3008-3031
- gallery: https://graph.heal7.com/#TIM-12
- raw js: https://graph.heal7.com/sku/TIM-12.js

```js
function(){
  const N = 80, ROWS = 4;
  const series = [];
  const names = ['CPU','MEM','NET','I/O'];
  const palette = [T.rose, T.gold, T.royal, T.teal];
  for(let r=0;r<ROWS;r++){
    const arr=[]; let v=0;
    for(let i=0;i<N;i++){ v = Math.sin(i/8 + r) * 30 + (Math.random()*20-10); arr.push([i, v]); }
    series.push({name:names[r], data:arr, color:palette[r]});
  }
  const opt = {
    backgroundColor:T.bg,
    tooltip:{trigger:'axis'},
    legend:{data:names, top:0, textStyle:{color:T.sub}},
    grid: series.map((_,i)=>({left:60, right:20, top: 24 + i*52, height: 44})),
    xAxis: series.map((_,i)=>({gridIndex:i, type:'value', show: i===series.length-1, axisLine:{lineStyle:{color:T.grid}}, axisLabel:{color:T.sub, fontSize:9}, max:N-1, min:0})),
    yAxis: series.map((s,i)=>({gridIndex:i, type:'value', min:-50, max:50, splitNumber:1, axisLine:{show:false}, axisTick:{show:false}, splitLine:{show:false}, axisLabel:{show:false}, name:s.name, nameLocation:'middle', nameGap:36, nameTextStyle:{color:T.sub, fontSize:11}})),
    series: series.flatMap((s,i)=>[
      {type:'line', name:s.name, xAxisIndex:i, yAxisIndex:i, data:s.data.map(d=>[d[0], Math.max(0,d[1])]), areaStyle:{color:s.color, opacity:.85}, lineStyle:{width:0}, symbol:'none', smooth:true},
      {type:'line', xAxisIndex:i, yAxisIndex:i, data:s.data.map(d=>[d[0], Math.min(0,d[1])]), areaStyle:{color:s.color, opacity:.4}, lineStyle:{width:0}, symbol:'none', smooth:true}
    ])
  };
  reg('c-horizon', opt);
}
```

