위어드섹터 공식 블로그

[프론트엔드] d3.js로 어떻게 곡선그래프를 구현할 수 있나요? 본문

Developing/프론트엔드

[프론트엔드] d3.js로 어떻게 곡선그래프를 구현할 수 있나요?

위어드섹터 2022. 10. 28. 15:51

 

<구현된 모습>

 

진행 중인 데이터너겟 프로젝트에서 d3를 활용한 곡선 그래프를 구현해야 했습니다. 

canvas 안에서는 html 코드를 삽입할 수 없어서 d3의 rect 를 이용하여 구현해보려고 시도해보았습니다. 하지만 두 가지 난관에 부딪혔습니다.

 

첫번째는 디자인과 똑같이 한쪽의 border-radius를 만드는 과정이었습니다. 그 이유가 양쪽의 border-radius는 속성 값으로 쉽게 변경할 수 있는데, 한쪽만 주는 경우는 svg의 path로 직접 좌표로 그려주어야 했기 때문입니다. 그 예시가 다음과 같습니다.

 

function rightRoundedRect(x, y, width, height, radius) { return "M" + x + "," + y + "h" + (width - radius) + "a" + radius + "," + radius + " 0 0 1 " + radius + "," + radius + "v" + (height - 2 * radius) + "a" + radius + "," + radius + " 0 0 1 " + -radius + "," + radius + "h" + (radius - width) + "z"; }​

(참고: 상단 함수는 타원의 곡선을 그리는 식 입니다. ) 

출처: https://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands

 

 

두 번째 난관은 d3에는 rect에 shadow를 주는 api가 없어서 다음과 같이 설정해야 했습니다.  

 
svg=d3.select("#chart")
        .append("svg")
        .attr("width",w)
        .attr("height",h);

// filter chain comes from:
// https://github.com/wbzyl/d3-notes/blob/master/hello-drop-shadow.html
// cpbotha added explanatory comments
// read more about SVG filter effects here: http://www.w3.org/TR/SVG/filters.html

// filters go in defs element
var defs = svg.append("defs");

// create filter with id #drop-shadow
// height=130% so that the shadow is not clipped
var filter = defs.append("filter")
    .attr("id", "drop-shadow")
    .attr("height", "130%");

// SourceAlpha refers to opacity of graphic that this filter will be applied to
// convolve that with a Gaussian with standard deviation 3 and store result
// in blur
filter.append("feGaussianBlur")
    .attr("in", "SourceAlpha")
    .attr("stdDeviation", 5)
    .attr("result", "blur");

// translate output of Gaussian blur to the right and downwards with 2px
// store result in offsetBlur
filter.append("feOffset")
    .attr("in", "blur")
    .attr("dx", 5)
    .attr("dy", 5)
    .attr("result", "offsetBlur");

// overlay original SourceGraphic over translated blurred opacity by using
// feMerge filter. Order of specifying inputs is important!
var feMerge = filter.append("feMerge");

feMerge.append("feMergeNode")
    .attr("in", "offsetBlur")
feMerge.append("feMergeNode")
    .attr("in", "SourceGraphic");

출처: http://bl.ocks.org/cpbotha/5200394

 

하지만 해당 코드를 실행해보니, box-shadow의 shift를 설정할 수 없어서 디자인 상의 box-shadow와 똑같은 디자인을 만드는 것이 불가능했습니다.

 

그래서 고안한 방법이 박스는 html, 디자인은 css로, 곡선은 canvas의 bezierCurveTo api를 활용하여 그리기로 하였습니다. 

 

출처: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/bezierCurveTo

 

여기서 문제가 발생했는데, 곡선의 꺾이는 각도를 어떻게 조절해야 하는지 몰랐습니다. 

그래서 노트에 시작점과 끝점을 기준으로 평행사변형을 그려보았습니다. 여기서 시작점과 끝점을 제외한 부분이 꺾이는 부분에 관여하는 부분임을 알게 되었습니다. 이렇게 규칙을 찾아서 문제를 해결했습니다. 

 

다음은 최종 구현한 이미지 입니다. 

 

 

결론: 

다음에는 디자인과는 조금 달라져도 최대한 비슷하게 곡선 그래프의 모든 과정을 d3로 구현해보고 싶습니다. 

 

 

 

 

블로그 구독자 문의 주소 : info@weirdsector.co.kr

그로스 해킹 파트너, LABBIT 바로가기

LABBIT을 운영하는 Team 위어드섹터 만나러 가기