csat-chart.js 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +381 -0
- package/LICENSE +21 -0
- package/README.md +369 -0
- package/dist/csat-chart.cjs +6171 -0
- package/dist/csat-chart.cjs.map +1 -0
- package/dist/csat-chart.d.cts +914 -0
- package/dist/csat-chart.d.mts +914 -0
- package/dist/csat-chart.mjs +6097 -0
- package/dist/csat-chart.mjs.map +1 -0
- package/dist/csat-chart.umd.min.js +4 -0
- package/dist/csat-chart.umd.min.js.map +1 -0
- package/package.json +66 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# 변경 기록 — csat-chart.js
|
|
2
|
+
|
|
3
|
+
## 1.2.0 — 2026-09-08
|
|
4
|
+
|
|
5
|
+
**범례가 아닌 글자가 캔버스 밖에서 잘리던 것을 고쳤다.** 1.1.1 이 「아직 남은
|
|
6
|
+
것」으로 적어 둔 그 목록이다. 데모의 산점도에서 세로축이 「총생산」으로 읽히던
|
|
7
|
+
것도 여기 있었다.
|
|
8
|
+
|
|
9
|
+
### 무엇이 잘렸나
|
|
10
|
+
|
|
11
|
+
1.1.1 의 계측을 그대로 쓰되 **글자 그리기만** 골라 냈다. 16종 × 두 자료(데모
|
|
12
|
+
표본·긴 한글 이름) × 두 범례 위치, 800×600 에서 72가지를 돌려 `fillText` 호출
|
|
13
|
+
1,962번을 기록했다. 자료 B 는 이번에 범례 이름뿐 아니라 **글자가 나오는 모든
|
|
14
|
+
자리**를 길게 채웠다 — 축 이름·축 단위·눈금 이름·제목·출처·각주·값 라벨·그림
|
|
15
|
+
안 점 이름·표 머리글.
|
|
16
|
+
|
|
17
|
+
72가지 중 **15가지**에서 글자가 캔버스를 벗어났고, 원인은 다섯 자리였다.
|
|
18
|
+
|
|
19
|
+
| 어디 | 무엇이 | 어느 쪽으로 | 어느 자료에서 |
|
|
20
|
+
| --- | --- | --- | --- |
|
|
21
|
+
| `canvas/axes.ts` 축 이름 | 「(명)」·「(°C)」 | 위 3.2px | 데모 표본 (꺾은선) |
|
|
22
|
+
| `canvas/axes.ts` 축 이름 | 「월평균 기온(°C)」 | 왼쪽 72.9px | 긴 이름 (기후) |
|
|
23
|
+
| `canvas/axes.ts` 축 이름 | 「월 강수량(mm)」 | 오른쪽 70.4px | 긴 이름 (기후) |
|
|
24
|
+
| `DeviationAGraph` 축 단위 | 「평년 대비 기온 차이(°C)」 | 왼쪽 129.5px | 긴 이름 |
|
|
25
|
+
| `DeviationAGraph` 축 단위 | 「평년 대비 강수량 차이(mm)」 | 오른쪽 181.1px | 긴 이름 |
|
|
26
|
+
| `ScatterBubble` y축 이름 | 「지역내총생산」 | 왼쪽 78.6px | 데모 표본·긴 이름 |
|
|
27
|
+
| `RadarChart` 축 이름 | 「주간 인구 지수」 | 왼쪽 59.7px | 긴 이름 |
|
|
28
|
+
| `CubeGraph` 축 이름 | 「1인당 지역내총생산」 | 오른쪽 57.1px | 긴 이름 |
|
|
29
|
+
| `CubeGraph` 점 이름 | 「서울특별시 강남구」 | 왼쪽 53.4px | 긴 이름 |
|
|
30
|
+
|
|
31
|
+
나머지 57가지는 처음부터 멀쩡했다 — 절댓값 막대·범주점·편차 B·하이서그래프·
|
|
32
|
+
인구 피라미드·누적 막대·삼각·트리맵·항목표·행렬표는 두 자료 어디에서도 글자가
|
|
33
|
+
캔버스를 넘지 않았다.
|
|
34
|
+
|
|
35
|
+
**재는 자부터 두 군데 틀렸다.**
|
|
36
|
+
|
|
37
|
+
- `fillText` 의 `maxWidth` 를 안 봤다. 제목·출처·각주는 이미 그 인자로 눌러
|
|
38
|
+
담기는데 재는 쪽이 원래 폭으로 셌다. 「긴 이름·오른쪽 범례」의 제목이
|
|
39
|
+
왼쪽으로 1.6px 넘쳤다는 보고는 여기서 나온 헛것이었다.
|
|
40
|
+
- **`actualBoundingBoxLeft/Right` 를 믿었다.** @napi-rs/canvas 는 글꼴 대체가
|
|
41
|
+
일어나면 그 두 값을 «첫 글꼴 조각만» 재서 돌려준다. 「평년 대비 강수량
|
|
42
|
+
차이(mm)」의 `width` 는 338.1 인데 오른쪽 끝은 한글 부분만 센 275.2 가
|
|
43
|
+
나온다 — 63px 을 못 본다. 그래서 가로는 **보내는 폭(`width`)과 `textAlign`**
|
|
44
|
+
으로 재고, 세로는 **글자마다 따로** 재서 가장 높은·낮은 것을 취하도록
|
|
45
|
+
바꿨다(한 글자는 언제나 한 조각이다). 위 표의 오른쪽 넘침 수치는 고친 자로
|
|
46
|
+
다시 잰 값이라 1.1.1 의 기록보다 크다.
|
|
47
|
+
|
|
48
|
+
### 어떻게 고쳤나
|
|
49
|
+
|
|
50
|
+
**이름은 자르지 않는다.** 1.1.1 과 같은 원칙이고, 쓸 수 있는 수의 순서도 같다 —
|
|
51
|
+
(1) 자리를 비운다, (2) 줄을 늘린다, (3) 그 글자만 글꼴을 줄인다(바닥 0.7배).
|
|
52
|
+
여기에 하나가 더 붙는다. (4) **여백에 떠 있는 글자는 안으로 민다.** 플롯 위
|
|
53
|
+
여백의 축 단위처럼 옆으로 밀어도 자료를 가리지 않는 글자가 있고, 그런 것에까지
|
|
54
|
+
플롯을 줄이는 것은 손해만 크다. 어느 수를 쓸지는 **그 글자가 무엇에 갇혀
|
|
55
|
+
있는가**로 갈렸다.
|
|
56
|
+
|
|
57
|
+
새 파일 `src/core/canvas/fit.ts` 가 그 넷을 재는 자다 — `textExtent`(글자가
|
|
58
|
+
덮는 자리), `nudgeInside`·`nudgeLinesInside`(안으로 밀기), `wrapToWidth`(접기),
|
|
59
|
+
`shrinkToWidth`(줄이기), `largestFitting`(들어가는 가장 큰 크기 찾기).
|
|
60
|
+
|
|
61
|
+
- **축 위 단위**(`axes.ts`·`DeviationAGraph`) — 플롯 **위 여백**에 떠 있다.
|
|
62
|
+
옆으로 밀거나 조금 내려도 자료를 안 가리므로 **민다.** 800px 짜리 위 띠에
|
|
63
|
+
좌·우 단위를 나란히 놓아도 서로 닿지 않는다. 캔버스보다 넓은 이름이면
|
|
64
|
+
그때만 글꼴을 줄인다.
|
|
65
|
+
- **산점도 y축 이름** — 눈금 숫자 열 **바깥**에 가로로 놓인다. 왼쪽 여백을
|
|
66
|
+
넓히면 되므로 **비운다**: `왼쪽 = max(130, 18 + 눈금숫자폭 + 이름폭 + 12)`.
|
|
67
|
+
이름 하나가 그림을 다 먹지 않게 이름 몫을 캔버스 너비의 30% 로 묶고, 그보다
|
|
68
|
+
길면 **접는다**. 「1인당 지역내총생산」은 「1인당 / 지역내총생산」 두 줄이 되어
|
|
69
|
+
왼쪽 여백이 130 → 232 로만 늘고 플롯은 그만큼만 줄어든다. 축 범위를 여백보다
|
|
70
|
+
먼저 잡도록 순서를 바꿨다 — 범위는 자료만으로 정해지므로 순환이 없다.
|
|
71
|
+
- **방사형 축 이름** — 꼭짓점 바깥(반지름 + 20)에 놓이므로 «여백을 넓힌다» 가
|
|
72
|
+
곧 «반지름을 줄인다» 다. 들어가는 가장 큰 반지름을 이분 탐색으로 찾는다
|
|
73
|
+
(반지름을 키우면 이름이 바깥으로 나가므로 단조롭다). 다만 그림이 5분의 1
|
|
74
|
+
넘게 줄어들 판이면 줄이기 전에 이름을 접는다 — 「주간 인구 지수」는 세 줄이
|
|
75
|
+
되고 반지름은 **그대로**다.
|
|
76
|
+
- **정육면체** — 축 이름 자리가 배율에 딸려 있으므로 같은 방식으로 배율을
|
|
77
|
+
이분 탐색한다. 점 이름은 유도선 끝에 붙으므로 **이름과 유도선 끝을 함께**
|
|
78
|
+
안으로 민다 — 유도선이 여전히 그 점을 가리키니 어느 점의 이름인지 흐려지지
|
|
79
|
+
않는다.
|
|
80
|
+
- **세로로 세운 축 이름**(편차 A) — 이것만 다르다. 한 글자씩 아래로 쌓으므로
|
|
81
|
+
**플롯의 «높이»에 갇힌다.** 좌우 여백을 아무리 넓혀도 자리가 안 늘고, 두
|
|
82
|
+
칸으로 세우면 어디부터 읽는지가 그림에 안 적힌다. 그래서 남는 수는 글꼴을
|
|
83
|
+
줄이는 것뿐이다 — 플롯 높이에 맞을 때까지 바닥(0.7배)까지 줄이고, 그래도
|
|
84
|
+
넘치면 플롯 위아래 여백까지 빌린다. 캔버스보다도 긴 이름이면 그때만 바닥을
|
|
85
|
+
깬다. 「평년 대비 월평균 기온 차이」(공백 뺀 열한 글자)가 28px → 24.2px 로
|
|
86
|
+
줄어 플롯 안에 든다. 짧은 이름(「기온 차이」)은 한 픽셀도 안 움직인다.
|
|
87
|
+
|
|
88
|
+
### 글꼴이 바뀌면 드러나던 자리
|
|
89
|
+
|
|
90
|
+
여기까지 고치고 CI 에 올렸더니 **하이서그래프에서 깨졌다.** 저자 기계에서는
|
|
91
|
+
3px 남고 통과하던 「(mm)」이 리눅스 러너의 대체 글꼴에서는 왼쪽으로 3.9px 을
|
|
92
|
+
넘었다. 이 검사가 **재는 기계의 글꼴 폭에 매달려 있다**는 뜻이고, 사용자에게도
|
|
93
|
+
실제 상황이다 — 이 라이브러리는 Noto 를 못 받으면 시스템 대체 글꼴로 그린다.
|
|
94
|
+
|
|
95
|
+
그래서 «폭이 25% 넓은 글꼴» 을 흉내 내는 판을 검사에 하나 더 두었다
|
|
96
|
+
(`measureText().width` 만 부풀리면 배치를 정하는 쪽과 재는 쪽이 함께 그 글꼴을
|
|
97
|
+
본다). CI 글꼴이 이미 5%쯤 넓으니 거기서는 사실상 1.3배로 돈다. 그 판으로 같은
|
|
98
|
+
종류가 일곱 자리 더 나왔고, 함께 고쳤다.
|
|
99
|
+
|
|
100
|
+
| 어디 | 무엇이 | 어떻게 |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| `Hythergraph` 축 단위 | 「(mm)」·「(°C)」 | 여백에 떠 있으므로 민다 |
|
|
103
|
+
| `DeviationBGraph` 지역 이름 | 「서울특별시 강남구」 | 아래 여백에 떠 있으므로 민다 |
|
|
104
|
+
| `CubeGraph` 제목 | 긴 제목 | 캔버스 폭에 맞게 줄이고 민다 |
|
|
105
|
+
| `ScatterBubble` 값 라벨 | 「(55, 60)」 | 오른쪽 끝 점의 값이라 민다 |
|
|
106
|
+
| `TernaryDiagram` 축 이름 | 「1차 산업 비중」 | 삼각형을 줄여 자리를 낸다 (방사형과 같은 방식) |
|
|
107
|
+
| `TernaryDiagram` 눈금 숫자 | 「100 (%)」 | 우하 꼭짓점 바깥이라 민다 |
|
|
108
|
+
| `PopulationPyramid` 축 단위 | 「(%)」·「(세)」 | 축 옆·위 여백에 떠 있으므로 민다 |
|
|
109
|
+
|
|
110
|
+
이제 72가지 × 두 글꼴 폭 어디에서도 글자가 캔버스를 넘지 않는다.
|
|
111
|
+
|
|
112
|
+
### 또 남은 것 — 아주 넓은 대체 글꼴의 눈금 숫자
|
|
113
|
+
|
|
114
|
+
같은 판을 **1.4배**로 걸어 보면 다음이 무너진다 — 하이서그래프·누적 막대·
|
|
115
|
+
인구 피라미드의 **세로축 눈금 숫자**가 왼쪽으로 0.7~6px 넘는다. 눈금 숫자는
|
|
116
|
+
축 이름·단위와 달리 눈금선에 붙어 있어 «밀기» 로는 못 고친다. 왼쪽 여백을
|
|
117
|
+
**잰 숫자 폭에 맞춰** 잡아야 하고, 그것은 산점도의 y축 이름에 한 손질을 세
|
|
118
|
+
종류에 더 하는 일이다. 제 글꼴에서도 CI 글꼴에서도 넘치지 않으므로 이 판에서는
|
|
119
|
+
손대지 않았다. 검사가 그 자리를 알고 있다 — `FONT_WIDTHS` 를 1.4 로 올리면
|
|
120
|
+
바로 드러난다.
|
|
121
|
+
|
|
122
|
+
**멀쩡하던 그림은 안 움직인다.** 여백은 예전 상수와 새로 잰 값 중 **큰 쪽**을,
|
|
123
|
+
반지름·배율은 예전 값과 새 값 중 **작은 쪽**을 쓴다. 미는 계산은 이미 안에 들어
|
|
124
|
+
있으면 받은 좌표를 그대로 돌려준다.
|
|
125
|
+
|
|
126
|
+
### 달라진 그림
|
|
127
|
+
|
|
128
|
+
골든 이미지 31장 중 **다섯 장**이 바뀌었고 나머지 26장은 바이트까지 같다.
|
|
129
|
+
갱신 전후를 눈으로 대조했다.
|
|
130
|
+
|
|
131
|
+
- `absbarZeroBaseline`·`categorydot`·`line` — 축 위 단위(「(mm)」·「(°C)」)의
|
|
132
|
+
여는 괄호 윗머리가 캔버스 맨 윗줄에 납작하게 잘려 있었다. 이제 둥근 머리가
|
|
133
|
+
다 보이고 위로 흰 여백이 남는다. 달라진 픽셀은 **y 0~32 안에만** 있다 —
|
|
134
|
+
플롯·눈금·자료는 그대로다.
|
|
135
|
+
- `climate` — 같은 이유로 좌·우 두 단위가 함께 내려왔다. 역시 y 0~32 안이다.
|
|
136
|
+
- `scatterDenseTicks` — 「Y축」이 왼쪽 끝에서 「축」의 반쪽만 남아 있었다. 이제
|
|
137
|
+
다 읽힌다. 눈금 숫자가 `1000000` 까지 가는 자료라 왼쪽 여백이 130 → 176 으로
|
|
138
|
+
늘었고 플롯이 그만큼 좁아졌다. 좁아진 만큼 x축 눈금 이름이 예전보다 하나씩
|
|
139
|
+
더 걸러져(1250 간격 → 1500 간격) 마지막 `10000` 이 안 적힌다 — 눈금 **선**은
|
|
140
|
+
그대로이고, 솎아내는 규칙 자체는 예전 것 그대로다.
|
|
141
|
+
|
|
142
|
+
데모 페이지의 산점도는 y축 이름을 **「1인당 지역내총생산」으로 되돌렸다.**
|
|
143
|
+
1.1.1 까지는 잘려서 「지역내총생산」으로 줄여 두었던 자리다. 두 줄로 접히므로
|
|
144
|
+
플롯 크기는 줄인 이름을 쓸 때와 같다.
|
|
145
|
+
|
|
146
|
+
### 무엇이 이 상태를 지키나
|
|
147
|
+
|
|
148
|
+
`test/core/legend-overflow.test.ts` 를 `test/core/overflow.test.ts` 로 옮기고
|
|
149
|
+
검사를 하나 더 두었다 — **범례든 축 이름이든 눈금 숫자든, 글자는 무엇이든
|
|
150
|
+
캔버스를 넘지 않는다.** 잉크 일반과 달리(그림은 경계를 넘어 그린 뒤 잘라 낼 수
|
|
151
|
+
있다) 읽히지 않는 글자를 그리는 것이 옳은 경우는 없으므로, 이 조건은 종류를
|
|
152
|
+
가리지 않고 걸 수 있다. 72가지(16종 × 두 자료 × 두 위치) × 두 글꼴 폭
|
|
153
|
+
= **288가지**를 지킨다. 케이스 파일도 `legend-overflow-cases.ts` →
|
|
154
|
+
`overflow-cases.ts` 로 옮겼다.
|
|
155
|
+
|
|
156
|
+
진단표는 `OVERFLOW_REPORT=경로.txt npx vitest run test/core/overflow.test.ts`
|
|
157
|
+
(`OVERFLOW_FONT_SCALE=1.25` 를 함께 주면 넓은 글꼴로 잰다).
|
|
158
|
+
|
|
159
|
+
## 1.1.1 — 2026-09-08
|
|
160
|
+
|
|
161
|
+
**범례가 캔버스 밖에서 잘리던 것을 고쳤다.** 데모의 방사형 그래프에서 범례
|
|
162
|
+
상자의 아래 테두리가 없어 보이던 그것이다.
|
|
163
|
+
|
|
164
|
+
### 왜 잘렸나
|
|
165
|
+
|
|
166
|
+
범례 상자는 이름을 잰 크기 그대로 놓였고, 캔버스에도 플롯에도 맞춰 보지
|
|
167
|
+
않았다. 두 갈래로 밖으로 나갔다.
|
|
168
|
+
|
|
169
|
+
- **아래 여백이 상자보다 얕았다.** 방사형은 하단 범례에 `70` 을 비워 두는데,
|
|
170
|
+
플롯 아래 `30` 을 띄우고 나면 남는 자리가 `40` 이다. 한 줄짜리 상자의 높이는
|
|
171
|
+
`(23.7 + 8) + 12 × 2 = 55.7` 이라 늘 `15.7` 이 모자랐다 — 이름이 무엇이든,
|
|
172
|
+
계열이 둘뿐이어도 잘렸다. 캔버스 아래로 넘친 잉크가 **6.5px**.
|
|
173
|
+
- **한 줄에 다 못 넣는 이름을 그대로 한 줄에 늘어놓았다.** 항목을 잇대어 놓고
|
|
174
|
+
상자 가운데로 모으는 방식이라, 줄이 상자보다 넓으면 **좌우로 똑같이**
|
|
175
|
+
삐져나갔다. 「서울특별시 강남구」 같은 이름 셋이면 절댓값 막대는 오른쪽으로
|
|
176
|
+
**5.8px**, 꺾은선은 왼쪽 **36.6px**·오른쪽 **57.6px** 이 캔버스 밖이었다.
|
|
177
|
+
|
|
178
|
+
찾는 방법은 픽셀이 아니라 **그리기 호출**을 봤다. 캔버스의 `fillText`·
|
|
179
|
+
`fillRect`·`strokeRect`·경로 그리기를 감싸 각 호출이 실제로 덮는 사각형을
|
|
180
|
+
기록하고, 그것을 캔버스·플롯·범례 상자와 견줬다. 가장자리에 닿는 잉크만 보면
|
|
181
|
+
플롯 틀을 넘었지만 캔버스 안에 머무는 범례를 놓치고(하이서그래프가 그랬다),
|
|
182
|
+
닿는 것이 정상인 축 테두리를 잘못 잡는다. 16종 × 두 자료(데모 표본·긴 한글
|
|
183
|
+
이름) × 두 위치(`bottom`·`right`)를 그렇게 돌렸다.
|
|
184
|
+
|
|
185
|
+
### 어떻게 고쳤나
|
|
186
|
+
|
|
187
|
+
**이름은 자르지 않는다.** 시험지 그림에서 「서울특별시」와 「서울특별시
|
|
188
|
+
강남구」가 같은 「서울특별…」로 보이면 문항 자체가 틀린다. 그래서 순서가 있다 —
|
|
189
|
+
(1) 줄을 늘리고 그만큼 높이를 미리 잡는다, (2) 항목 **하나**가 통째로 한 줄보다
|
|
190
|
+
넓을 때만 범례 글꼴을 `11px` 까지 줄인다, (3) 그래도 안 되면 잘라 내지 않고
|
|
191
|
+
그대로 둔다(16종 어디에서도 여기까지 오지 않는다).
|
|
192
|
+
|
|
193
|
+
- `layoutBottomLegend()` 가 상자 너비에 맞춰 항목을 **여러 줄로 접는다.** 한
|
|
194
|
+
줄로 끝나는 경우의 좌표는 예전과 **정확히 같다** — 줄 간격과 상자 높이가 줄
|
|
195
|
+
수에만 붙어 있다.
|
|
196
|
+
- `measureBottomLegend()` 를 새로 두어 여백을 잡는 쪽이 **그리기 전에** 상자
|
|
197
|
+
높이를 물어보게 했다. 렌더러 열은 기존 상수와 이 값 중 **큰 쪽**을 쓴다.
|
|
198
|
+
이름이 짧아 한 줄로 끝나면 상수가 이겨 예전 그대로고, 줄이 늘거나 상수가
|
|
199
|
+
모자랄 때만 플롯이 그만큼 줄어 자리를 낸다.
|
|
200
|
+
- `drawLegend()`·`drawInsideLegend()` 는 이제 캔버스 크기를 받는다. 호출부의
|
|
201
|
+
여백 계산이 어긋나도 상자가 캔버스 밖으로는 나가지 않는다.
|
|
202
|
+
- 플롯 **안쪽** 범례는 상자가 플롯보다 넓어지면 글꼴을 줄여 폭을 맞춘다.
|
|
203
|
+
예전에는 오른쪽 정렬 시작점이 플롯 왼쪽 밖으로 밀려났다.
|
|
204
|
+
- **하이서그래프는 이 배치 코드를 복사해 갖고 있었다.** 같은 버그가 그대로
|
|
205
|
+
있었으므로(도시 이름 셋이면 좌우로 56.2px 씩 플롯 틀을 넘는다) 접는 계산을
|
|
206
|
+
공용 함수로 넘겨 함께 고쳤다. 아이콘 너비·간격은 제 값을 그대로 쓰므로 한
|
|
207
|
+
줄짜리 그림은 달라지지 않는다.
|
|
208
|
+
|
|
209
|
+
### 달라진 그림
|
|
210
|
+
|
|
211
|
+
골든 이미지 31장 중 **`radar` 한 장만** 바뀌었고 나머지 30장은 바이트까지
|
|
212
|
+
같다. 갱신 전후를 눈으로 대조했다 — 예전 그림은 범례 상자의 아래 테두리가
|
|
213
|
+
아예 없고 「(가)」·「(나)」가 캔버스 맨 밑줄에 붙어 있었다. 새 그림은 네 변이
|
|
214
|
+
다 있고 글자 위아래로 여백이 남는다. 방사형 자체는 아래 여백이 `80` → `87.7`
|
|
215
|
+
로 늘어난 만큼(7.7px) 조금 작아졌고, 축 이름·눈금 숫자·계열 모양은 그대로다.
|
|
216
|
+
|
|
217
|
+
`test/core/overflow.test.ts`(당시 이름 `legend-overflow.test.ts`) 가 이 상태를
|
|
218
|
+
지킨다 — 16종 × 두 자료 × 두 위치에서 범례의 그리기 호출이 캔버스를 벗어나면
|
|
219
|
+
실패한다.
|
|
220
|
+
|
|
221
|
+
### 여기서 안 고친 것 — 1.2.0 에서 고쳤다
|
|
222
|
+
|
|
223
|
+
이 판이 고친 것은 **범례**뿐이었다. 같은 계측에서 범례가 아닌 글자가 캔버스를
|
|
224
|
+
벗어나는 자리도 드러났고 그때는 손대지 않았다 — 산점도의 세로축 이름
|
|
225
|
+
(왼쪽 77.6px), 기후·꺾은선의 축 단위(위 3.2px·긴 이름이면 왼쪽 72.9px),
|
|
226
|
+
편차 A 의 축 단위(좌우 129.5px), 방사형의 긴 축 이름(왼쪽 59.7px).
|
|
227
|
+
축 이름을 어디까지 밀어낼지는 범례와 다른 판단이 필요했기 때문이다.
|
|
228
|
+
**1.2.0 이 그 판단을 내리고 전부 고쳤다.**
|
|
229
|
+
|
|
230
|
+
## 1.1.0 — 2026-09-08
|
|
231
|
+
|
|
232
|
+
굵던 막대를 줄이고, 이미 있던 「가로 막대」를 데모에서 고를 수 있게 했다.
|
|
233
|
+
**렌더러(`src/core`)를 처음으로 고친 판이다.** 1.0.0 은 원본을 한 글자도 고치지
|
|
234
|
+
않고 옮긴 판이었고 골든 이미지 31장이 그 증거였다 — 이식이 끝나 판이 나갔으므로,
|
|
235
|
+
이제 골든은 «이식이 옳았다» 의 증거가 아니라 «렌더 결과가 언제 어떻게
|
|
236
|
+
달라졌는가» 의 기준이다. 그 내역은 이 파일이 판마다 적는다.
|
|
237
|
+
|
|
238
|
+
### 달라진 그림
|
|
239
|
+
|
|
240
|
+
- **100% 누적 막대(`stacked`)의 막대를 절반으로 줄였다.** 칸의 60% → **30%**,
|
|
241
|
+
그리고 없던 **84px 상한**을 두었다. 800×600·범주 셋에서 132px → 66px 이다.
|
|
242
|
+
상한이 `absbar` 의 80px 이 아니라 84px 인 것은 이 막대만 칸 **안** 에 계열
|
|
243
|
+
이름을 적기 때문이다 — 네 글자 이름이 19.8px 굵은 명조로 76.5px 이고
|
|
244
|
+
`drawSegmentLabel` 이 좌우로 6px 을 비우므로 82.5px 이 있어야 눌리지 않는다.
|
|
245
|
+
가로 막대(`barDirection: 'horizontal'`)의 두께도 같은 규칙을 쓴다 — 방향을
|
|
246
|
+
바꿨다고 굵기 관습이 달라지면 안 된다(86px → 43px).
|
|
247
|
+
- **편차 B(`deviation-b`)의 막대에 80px 상한을 두었다.** 지역이 셋이면 칸의
|
|
248
|
+
절반이 이미 90px 이라 `absbar` 가 쓰던 상한을 넘었다. 90px → 80px, 둘이면
|
|
249
|
+
135px → 80px.
|
|
250
|
+
- **편차 A(`deviation-a`)의 막대에도 같은 80px 상한을 두었다.** 열두 달
|
|
251
|
+
(24.75px)·넉 달(74.25px)에서는 걸리지 않아 그림이 그대로고, `monthInterval: 2`
|
|
252
|
+
일 때만 148.5px → 80px 로 준다 — 칸이 둘뿐이라 누적 막대보다도 굵었다.
|
|
253
|
+
- `absbar` 는 손대지 않았다. 80px 상한은 원래 그 렌더러에 있던 값이고, 위 셋이
|
|
254
|
+
그것을 따라간 것이다.
|
|
255
|
+
|
|
256
|
+
골든 이미지는 31장 중 `stacked`·`stackedExam`·`deviationB` 셋만 바뀌었고 나머지
|
|
257
|
+
28장은 바이트까지 같다. 바뀐 세 장은 갱신 전후를 눈으로 대조했고, 달라진 픽셀이
|
|
258
|
+
전부 플롯 «안쪽» 에 있음을 좌표로도 확인했다 — 축·눈금·범주 이름·범례·제목은
|
|
259
|
+
한 픽셀도 움직이지 않았다. `stackedExam` 의 칸 안 이름(A·B·전남·경남·기타)은
|
|
260
|
+
66px 막대에서도 눌리지 않는다(두 글자 38.3px, 쓸 수 있는 폭 60px).
|
|
261
|
+
|
|
262
|
+
### 데모 (`docs/index.html`)
|
|
263
|
+
|
|
264
|
+
- 카드에 **「막대 방향」(세로/가로)** 조작칸을 더했다. 라이브러리는 처음부터
|
|
265
|
+
`AbsBarGraphData.barDirection`·`StackedGraphData.barDirection` 을 받고 두
|
|
266
|
+
렌더러가 그대로 따르는데, 데모가 그 칸을 내놓지 않았을 뿐이다. 그러니
|
|
267
|
+
라이브러리 쪽 변경은 없다.
|
|
268
|
+
- 그 칸이 있는 자료는 `absbar`·`stacked` 둘뿐이라 나머지 열넷에는 그리지 않는다.
|
|
269
|
+
`options` 가 아니라 **자료** 를 고치는 칸이라, 옵션칸 아래 «그래프 안의 이름»
|
|
270
|
+
과 같은 자료칸 쪽에 가로줄로 갈라 둔다. `stacked` 을 `displayMode: 'pie'` 로
|
|
271
|
+
바꾸면 원 그래프는 방향을 읽지 않으므로 칸이 사라진다 — 켜도 안 변하는
|
|
272
|
+
스위치를 내놓지 않는다는 이 페이지의 규칙 그대로다.
|
|
273
|
+
|
|
274
|
+
## 1.0.0 — 2026-09-08
|
|
275
|
+
|
|
276
|
+
첫 공개. 지리 수업·평가 자료를 만들려고 쓰던 렌더러를 독립 패키지로 떼어냈다.
|
|
277
|
+
|
|
278
|
+
### 담긴 것
|
|
279
|
+
|
|
280
|
+
- Canvas 렌더러 16종 (기후·인구 피라미드·삼각·100% 막대/원·절댓값 막대·산점도/버블·
|
|
281
|
+
하이서그래프·정육면체·방사형·편차 A/B·꺾은선·범주점·트리맵·계단식 행렬표·항목×지역 표)
|
|
282
|
+
- 파사드 `CsatChart` — `type` 문자열 하나로 16종을 그리고, 만든 뒤에도 `update()`·
|
|
283
|
+
`resize()`·`toDataURL()`·`download()`·`destroy()` 로 다룬다
|
|
284
|
+
- `ensureFonts()` — 시험지 글꼴을 선택적으로 불러온다
|
|
285
|
+
- ESM·CJS·UMD 세 산출물, 런타임 의존성 0
|
|
286
|
+
|
|
287
|
+
### 알아 둘 동작
|
|
288
|
+
|
|
289
|
+
렌더러(`src/core`)는 옮겨 오면서 한 글자도 고치지 않았고 — 이 판 이야기다,
|
|
290
|
+
1.1.0 에서 처음 고쳤다 — 그것을 감싸는 파사드·검증·글꼴 계층을 이 패키지에서
|
|
291
|
+
새로 썼다. 그 계층이 하는 일은 다음과 같다.
|
|
292
|
+
|
|
293
|
+
- **모양 검증, 한국어 메시지.** `new CsatChart()`·`update()` 에 준 `data` 가
|
|
294
|
+
기본 데이터와 모양이 다르면(키가 빠졌다, 값의 자료형이 다르다, 길이가 고정된
|
|
295
|
+
배열의 길이가 다르다) 그리기 전에 막고 이유를 한국어로 말한다. 배열은 원소의
|
|
296
|
+
자료형까지 한 겹 더 본다 — `months: [1, 2, …, 12]` 처럼 열두 달 자료를 숫자
|
|
297
|
+
배열로 적는 실수가 흔하다. 배열이고 길이도 12라 겉모양만 보면 통과할 법한데,
|
|
298
|
+
그대로 그리면 브라우저에서는 좌표가 어긋나 빈 그림이 되고 Node 캔버스에서는
|
|
299
|
+
프로세스가 죽는다. 이 한 겹이 그 실패를 그리기 전에 막는다.
|
|
300
|
+
- **크기 없는 `<canvas>` 는 800×600 으로 채운다.** HTML 기본값인 300×150 은 이
|
|
301
|
+
라이브러리의 절대 픽셀 여백·글자 크기와 맞지 않아 제목·눈금·각주가 겹친 읽을 수
|
|
302
|
+
없는 그림이 된다. `<canvas id="c"></canvas>` 라고만 쓰는 실수는 흔한데, 그 결과가
|
|
303
|
+
조용한 오작동이면 안 되므로 자동으로 800×600 을 넣는다. 명시적으로 준 크기는
|
|
304
|
+
그대로 존중한다.
|
|
305
|
+
- **`roundRect` 폴리필로 지원 환경을 도로 넓힌다.** `src/core` 중 `legend.ts`·
|
|
306
|
+
`Hythergraph.ts`·`ScatterBubble.ts` 셋이 범례 박스를 그릴 때 `ctx.roundRect()`
|
|
307
|
+
를 쓰는데, 이 메서드는 core 안의 유일한 최신 Canvas 2D API 라 Chrome 99·
|
|
308
|
+
Firefox 112·Safari 16.4(iOS 16.4) 아래에서는 없다 — 그 아래에서는 범례가 있는
|
|
309
|
+
여덟 종류(`absbar`·`climate`·`deviation-a`·`deviation-b`·`hythergraph`·
|
|
310
|
+
`pyramid`·`scatter`·`stacked`)가 `ctx.roundRect is not a function` 으로 죽어
|
|
311
|
+
캔버스가 하얗게 비었다. 이 라이브러리가 겨냥하는 학교 PC·구형 iPad 가 정확히
|
|
312
|
+
그 아래 버전을 쓴다. `CsatChart` 생성자가 첫 렌더 전에
|
|
313
|
+
`installRoundRectPolyfill()` 을 자동으로 불러 이 여덟 종류를 도로 원래
|
|
314
|
+
지원 환경(Chrome 80·Firefox 74·Safari 13.1)에서 그릴 수 있게 한다. 저수준
|
|
315
|
+
렌더러를 파사드 없이 직접 부르는 사람은 `installRoundRectPolyfill()` 을
|
|
316
|
+
그리기 전에 스스로 한 번 불러야 한다 — 이 함수도 공개 표면에서 내보낸다.
|
|
317
|
+
- **`toDataURL({ scale })` — 인쇄용 고해상도 내보내기.** `resize()` 는 캔버스만
|
|
318
|
+
키우고 글자 크기는 절대 픽셀 그대로라 «두 배로 선명한 같은 그림» 이 아니라
|
|
319
|
+
«글자가 절반으로 작아진 다른 그림» 이 된다. `scale` 은 글자·선까지 함께 키워
|
|
320
|
+
뽑은 뒤 캔버스를 원래 크기로 되돌린다. `download()` 도 같은 옵션을 받는다.
|
|
321
|
+
- **글꼴이 늦게 도착하면 한 번 다시 그린다.** `ensureFonts()` 를 깜빡하는 실수가
|
|
322
|
+
가장 흔한데 증상이 조용하다 — 대체 글꼴로 «멀쩡히» 그려지고 잠시 뒤 글꼴이
|
|
323
|
+
와도 아무도 다시 그리지 않는다. `CsatChart` 는 `document.fonts.ready` 를
|
|
324
|
+
지켜보다 한 번 더 그린다. 다만 만능은 아니다 — `document.fonts.ready` 는
|
|
325
|
+
**부르는 시점에** 로딩 중이던 것이 끝나면 이행하므로, 차트를 먼저 만들고
|
|
326
|
+
`ensureFonts()` 를 나중에 부르면 이 약속은 이미 이행된 뒤라 도움이 안 된다.
|
|
327
|
+
`await CsatChart.ensureFonts()` 뒤에 차트를 만드는 순서를 지키는 것이 여전히
|
|
328
|
+
옳다.
|
|
329
|
+
- **상수는 얼려서 내보낸다.** `CHART_TYPES`·`AGE_GROUPS`·`DOT_MARKER_ORDER`·
|
|
330
|
+
`LINE_MARKER_ORDER`·`LINE_STYLE_ORDER`·`MONTH_LABELS_EN`·`MONTH_LABELS_NUM`·
|
|
331
|
+
`LINE_DASH` 는 렌더러가 기본값으로 읽는 바로 그 객체다. 타입 검사를 받지 않는
|
|
332
|
+
CDN 사용자를 겨냥한 패키지라, 얼리지 않으면 `DOT_MARKER_ORDER.reverse()` 한
|
|
333
|
+
번에 이후 모든 그림의 기호 배정이 조용히 어긋난다.
|
|
334
|
+
- **저수준 렌더러 하나만 가져오면 훨씬 가볍다.** 라이브러리 전부는 95.9 KB,
|
|
335
|
+
`CsatChart` 파사드만 가져와도 94.6 KB 다 — 파사드가 `type` 문자열로 16종을
|
|
336
|
+
고르는 이상 전부를 붙들고 있어야 한다. 렌더러 하나만 가져왔을 때의 크기는
|
|
337
|
+
종류마다 다르다 — 16종 전체를 재 보면 `renderMatrixTable` 이 3.9 KB 로 가장
|
|
338
|
+
작고 `renderScatterGraph` 가 16.3 KB 로 가장 크다. `renderClimateGraph` 는
|
|
339
|
+
10.0 KB 다 — README 「저수준 렌더러」에 실린 그 예시다. (esbuild
|
|
340
|
+
`--bundle --minify --format=esm` 실측.)
|
|
341
|
+
- `sourceInline`·`sourceLeft` 는 공통 옵션이지만 지금은 각각 `scatter`·`stacked`
|
|
342
|
+
에서만 동작한다. 나머지 종류는 렌더러가 그 옵션을 넘기지 않는다.
|
|
343
|
+
- `pyramid` 의 `sexFills` 는 막대 채움색만 바꾼다. 범례 사각형은 `sexFills` 와
|
|
344
|
+
무관하게 항상 `#666`(남)·`#BBB`(여) 로 고정돼 있다(`src/core/graphs/
|
|
345
|
+
PopulationPyramid.ts`, 막대는 약 199·206번째 줄, 범례는 약 369~370번째 줄).
|
|
346
|
+
시험지 관습대로 남·여를 같은 연회색 한 가지로 두면(`sexFills: ['#d9d9d9',
|
|
347
|
+
'#d9d9d9']`) 막대는 구분이 없는데 범례만 진하고 연한 두 회색을 보여주는
|
|
348
|
+
어긋남이 생긴다. 그런 조합에서는 `options.showLegend: false` 로 범례를 꺼
|
|
349
|
+
두는 것이 지금은 유일한 우회다 — 어차피 단일 채색 피라미드는 범례가 없어도
|
|
350
|
+
`남`/`여` 축 머리글로 이미 구분된다.
|
|
351
|
+
- `absbar` 의 `options.showDataLabels` 는 **누적 막대에서만 제대로 보인다**. 값
|
|
352
|
+
글자의 색을 `isLightFill(계열번호) ? '#000' : '#fff'` 로 정하는데
|
|
353
|
+
(`src/core/graphs/AbsBarGraph.ts`, 묶음 막대는 세로 190번째 줄·가로 354번째
|
|
354
|
+
줄), `isLightFill` 은 채움색이 아니라 **계열 번호**만 보고 `index >= 3` 을
|
|
355
|
+
돌려준다(`src/core/canvas/patterns.ts` 206번째 줄). 그래서 앞 세 계열은 늘
|
|
356
|
+
흰 글자다. 누적이 아닌 묶음 막대(`stacked: false`)는 그 글자를 막대 **바깥**
|
|
357
|
+
에 그리므로(세로는 막대 위 `by - 4`, 가로는 막대 오른쪽 `bx + bw + 4`) 흰
|
|
358
|
+
바탕에 흰 글씨가 되어 보이지 않는다 — 계열이 셋 이하인 묶음 막대에서는 이
|
|
359
|
+
옵션을 켜고 끈 PNG 가 바이트까지 같다. 누적 막대(`stacked: true`, 같은 파일
|
|
360
|
+
161·325번째 줄)는 글자가 채움 **안** 에 놓이므로 흰 글자가 오히려 맞고 제대로
|
|
361
|
+
보인다. 우회는 두 가지다 — `stacked: true` 로 쌓거나, 계열을 넷 이상 두어
|
|
362
|
+
넷째 계열부터(`index >= 3`) 검은 글자를 얻는 것. `seriesFills` 로 채움을
|
|
363
|
+
밝게 해도 글자색은 바뀌지 않는다. 계열 번호만 보기 때문이다.
|
|
364
|
+
|
|
365
|
+
### 원본과 다른 점
|
|
366
|
+
|
|
367
|
+
이 렌더러는 원래 지리 수업 도구인 GeoTester·GeoGrapher 안에 있었다. 그 두
|
|
368
|
+
앱에서 옮겨 오는 경우에만 해당하는 이야기다.
|
|
369
|
+
|
|
370
|
+
- **`canvas/export.ts` 를 옮기지 않았다.** GeoGrapher 전용이다. 파일명을
|
|
371
|
+
`GeoGrapher_*.png` 로 짓고, 이 패키지가 공개하지 않는 `ExportSettings`·`GraphType`
|
|
372
|
+
에 의존하며, 원본 앱에서도 아무도 부르지 않는 죽은 코드였다. 같은 역할은
|
|
373
|
+
`CsatChart.toDataURL()`·`download()` 가 대신한다.
|
|
374
|
+
- **공개 표면에서 GeoGrapher UI 의 관심사를 뺐다.** `GraphType`(지도 4종과 `guide`
|
|
375
|
+
를 담은 메뉴 목록)·`GRAPH_LABELS`·`MAP_GRAPH_TYPES`·`isMapGraphType`·
|
|
376
|
+
`ExportSettings`·`createDefaultExportSettings` 는 내보내지 않는다. 그리는 코드는
|
|
377
|
+
그대로 있으므로 렌더 결과는 같다.
|
|
378
|
+
- 렌더러 파일 자체는 이 판까지 **한 글자도 고치지 않았다.** 골든 이미지 31장이
|
|
379
|
+
그 증거였다. 그 뒤로 렌더 결과가 달라진 내역은 이 파일의 판마다 적는다.
|
|
380
|
+
- 원본에서 인구 피라미드의 눈금 간격 계산이 한 번 갈라진 적이 있는데, 이
|
|
381
|
+
패키지는 개선된 쪽을 가져왔다.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 김용현
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|