전체 물량 대비해서 현재 물량(진행율)을 확인하기 위해 그래프를 겹쳐야 했다
기존에 Chart.js를 쓰고 있었는데 아무리 찾아봐도 저런 모양은 없음 🤔
없으면 만들어야지~ 💁♀️
뒤에 투명한 막대가 전체물량, 채워져있는 막대가 현재물량이다
보이는 막대의 수는 2개지만 겹친 모양을 구현하기 위해 4개를 만들었다
const data: ChartData<"bar", number[], string> = {
labels: clickData && click ? label : chartData.name,
datasets: [
{
type: "bar",
label: "BIM물량",
data:
clickData && click
? [clickData.current.data]
: chartData.approveBHSum,
backgroundColor: "#1423A3",
stack: "Stack 0",
barThickness: 28,
maxBarThickness: 20,
},
{
type: "bar",
label: "목표물량",
data: clickData && click ? [clickData.target.data] : chartData.totalSum,
backgroundColor: "#1423a314",
stack: "Stack 0",
borderWidth: 0.1,
borderColor: "#1a4bd180",
barThickness: 28,
maxBarThickness: 20,
},
{
type: "bar",
label: "샵 물량",
data: clickData && click ? [clickData.shop.data] : chartData.shopSum,
backgroundColor: "#638AF4",
stack: "Stack 1",
barThickness: 28,
maxBarThickness: 20,
},
{
type: "bar",
label: "추정물량",
data: clickData && click ? [clickData.estimate.data] : chartData.preSum,
backgroundColor: "#638af414",
stack: "Stack 1",
borderWidth: 0.1,
borderColor: "#638af466",
barThickness: 28,
maxBarThickness: 20,
},
],
};
여기서 중요한 것은 stack
을 맞춰주면 같은 stack
끼리 겹쳐지게 된다!
Chart.js 문서 : Stacked Bar Chart with Groups
<Bar
ref={chartRef}
onClick={click ? () => undefined : onClickBar}
data={data}
style={
click
? { cursor: "pointer" }
: { maxWidth: 950, maxHeight: 320, cursor: "pointer" }
}
options={{
// responsive: false,
interaction: {
intersect: false,
mode: "index",
axis: "x",
},
indexAxis: "x",
elements: {
bar: {
borderRadius: 4,
borderWidth: 0,
},
},
scales: {
x: {
stacked: true,
grid: {
display: false,
},
ticks: {
font: { size: 12, weight: 500 },
color: "#7D7B88",
callback: function (value, index, values) {
if (data.labels === undefined) return;
const label = data.labels[index];
return typeof label === "string" && label.length > 6
? label.substring(0, 6) + "..."
: label;
},
},
},
y: {
stacked: false,
ticks: {
font: { size: 12, weight: 500 },
color: "#7D7B88",
},
},
},
plugins: {
legend: {
display: false,
},
datalabels: {
clamp: true,
color: "white",
font: { size: 20 },
display: false,
anchor: "end",
align: "start",
rotation: 270,
},
tooltip: {
enabled: true,
position: "average",
yAlign: "center",
// padding: 12,
cornerRadius: 8,
// bodySpacing: 4,
boxPadding: 3,
boxWidth: 10,
boxHeight: 10,
// titleMarginBottom: 10,
// footerMarginTop: 10,
callbacks: {
afterFooter: function (context) {
const bhRaw = (context[0] && context[0].raw) as number;
const targetRaw = (context[1] && context[1].raw) as number;
const shopRaw = (context[2] && context[2].raw) as number;
const preRaw = (context[3] && context[3].raw) as number;
const bimPer = (bhRaw / targetRaw) * 100;
const shopPer = (shopRaw / preRaw) * 100;
return bar === "floor"
? `BIM비율(%) ${isNaN(bimPer) ? "0.0" : bimPer.toFixed(1)}%\n샵비율(%) ${isNaN(shopPer) ? "0.0" : shopPer.toFixed(1)}%`
: "";
},
},
},
},
}}
/>
코드가 길지만.......
여기서 봐야할 것은 options
> scales
> x
> stacked: true
이다
이걸 설정해줘야 x축으로 그래프가 쌓인다
라인 그래프도 마찬가지이다
전체 진행율을 점선으로 표시하고 현재 진행율을 실선으로 표시해야 했다
처음엔 line을 사용했는데 제대로 된 데이터가 찍히지 않아서 scatter로 바꿨다
이유는 line 차트는 진행율을 표시하기는 어렵다고 판단했기 때문이다
y축에는 물량, x축에는 진행율을 체크해야 하는데 진행율은 당연히 상승하는데
line 차트는 x, y축 데이터가 모두 필요한게 아니라 x축에는 label만 표시하고 실제 값은 y축으로 표시하기 때문이었다!!!!!
label을 숫자로 인식하지 못하고 무조건 문자로 인식함.....🤦♀️
그래서 x, y의 값을 모두 필요로 하는 scatter로 변경 완 ✅
대신 값을 다루는 것이 조금 복잡해졌다
xValues 진행율
shValues 실제 샵물량(파랑실선)
bhValues 실제 BIM물량(초록실선)
shop 전체 샵물량(파랑점선)
target 전체 BIM물량(초록점선)
이렇게 number[]
타입의 데이터들을 받아와서 프론트에서 값을 가공했다
const createDataPoints = (xValues: number[], yValues: number[]) => {
return xValues
.map((xValue, index) => ({
x: xValue,
y: yValues[index],
}))
.filter((point) => point.y !== null);
};
x, y 값이 모두 필요하기 때문에 2개의 파라미터를 받는 함수를 만들어서 배열을 리턴했다
const shopData = createDataPoints(xValues, shValues);
const bimData = createDataPoints(xValues, bhValues);
const targetData = createDataPoints(xValues, target);
const shopEstimateData = createDataPoints(xValues, shop);
진행율은 항상 필요하기 때문에 넣어주고 각 데이터들을 사용했다
const chartData: ChartData<
"scatter",
{ x: number; y: number | undefined }[],
number
> = {
labels: xValues,
datasets: [
{
type: "scatter",
label: "샵 물량",
data: shopData,
backgroundColor: "#638AF4",
borderWidth: 1,
borderColor: "#638AF4",
stack: "Stack 0",
pointBorderWidth: 5,
showLine: true,
},
{
type: "scatter",
label: "BIM 물량",
data: bimData,
backgroundColor: "#3BC58E",
borderWidth: 1,
borderColor: "#3BC58E",
stack: "Stack 1",
pointBorderWidth: 5,
showLine: true,
},
{
type: "scatter",
label: "목표물량",
data: targetData,
backgroundColor: "#3BC58E",
borderWidth: 1,
borderColor: "#3BC58E",
borderDash: [8, 3],
stack: "Stack 1",
pointStyle: "dash",
showLine: true,
},
{
type: "scatter",
label: "추정물량",
data: shopEstimateData,
backgroundColor: "#638AF4",
borderWidth: 1,
borderColor: "#638AF4",
borderDash: [8, 3],
stack: "Stack 0",
pointStyle: false,
showLine: true,
},
{
type: "scatter",
label: "실행예산",
data: workingData,
backgroundColor: "#FF7777",
borderWidth: 1,
stack: "Stack 2",
borderColor: "#FF7777",
borderDash: [8, 3],
pointStyle: false,
showLine: true,
},
],
};
이것도 stack
을 맞춰주면 겹쳐보이는 그래프 구현 완! ✅
borderDash
와 borderColor
, showLine
을 설정해주고
현재 물량에만 점을 찍어주기 위해 pointStyle
여부를 입력했다
마땅한 라이브러리가 있는지 찾아보는데 시간이 좀 걸렸고
데이터를 제대로 출력하기 위해서 생각하느라 또 시간을 썼다
그래도 그런 시간을 가졌기 때문에 이런 예쁜 그래프가 탄생되어서
나름 흐뭇하고 뿌듯한 페이지라고 생각한다 💪