안녕하세요, 개발자 Sean입니다.
오늘은 원형차트 ui 관련 내용을 포스팅하려고 합니다. 아래 사진의 우측 상단에 들어가는 조그마한 차트입니다.
사실 팀에 합류하고 차트 ui 제작을 해본 경우는 매우 드문데요, 왜냐하면 외부 라이브러리(highchart)를 이용하는 편이 훨씬 생산성이 높기 때문에 직접 차트 ui를 만들지 않았습니다. 하지만 이번에 개편되는 ui 디자인의 경우 외부 라이브러리를 이용하면 디테일한 부분의 스타일을 수정해주어야 하는 번거로움이 생기는데 반해 직접 구현하기에 어렵지 않은 디자인이라 직접 만들어 보기로 했습니다.
리엑트와 스타일 컴포넌트를 이용했습니다.
우선은 아래와 같이 원형차트 컴포넌트 파일을 만들어 줍니다. 여기서 저는 이름을 CircleChart라고 지었는데 보통은 PieChart라고 부른다고 알고 있습니다. 저의 경우 이미 PieChart가 있어 구분해 주기 위해 아래와 같이 이름을 지었습니다.
//CircleCharts.js
import {
Container,
} from './CircleChartStyles';
const CircleChart = ({size='30px', percent=0}) => {
return (
<Container
size={size}
>
//
</Container>
)
}
export default CircleChart;
import styled from "styled-components";
export const Container = styled.div`
position: relative;
width: ${props => props.size};
height: ${props => props.size};
display: flex;
justify-content: center;
align-items: center;
`
해당 ui는 두개의 인자를 받게 될 겁니다. 하나는 크기를 설정하는 size 그리고 나머지 하나는 차트에서 표현할 데이터인 percent 값입니다.
다음으로 두 개의 스타일 태그를 만드는데 하나는 OutCircle이고 다른 하나는 InnerCircle입니다. 외부에 있는 원은 외부의 옆은 녹색을 표현하기 위해 필요하고 InnerCircle은 내부에 퍼센트 값만큼 채워지는 짙은 녹색을 위한 스타일 태그입니다.
import {
Container,
OutCircle,
InnerCircle,
} from './CircleChartStyles';
const CircleChart = ({size='30px', percent=0}) => {
return (
<Container
size={size}
>
<OutCircle size={size}>
<InnerCircle
size={size}
percent={percent}
/>
</OutCircle>
</Container>
)
}
export default CircleChart;
import styled from "styled-components";
export const Container = styled.div`
position: relative;
width: ${props => props.size};
height: ${props => props.size};
display: flex;
justify-content: center;
align-items: center;
`
export const OutCircle = styled.div`
border-radius: 50%;
background-color: #BCCCBF;
width: ${props => props.size};
height: ${props => props.size};
display: flex;
justify-content: center;
align-items: center;
mask-image: radial-gradient(circle farthest-side at center, transparent 70%, white 50%);
background-clip: text;
`
export const InnerCircle = styled.div`
width: ${props => props.size};
height: ${props => props.size};
background: conic-gradient(${props => `#16A799 0deg ${props.percent*3.6}deg`}, ${props => `transparent ${props.percent*3.6}deg 360deg`});
`
외부의 원부터 살펴보겠습니다.
디자인을 보면 가운데 원이 흰색이 아닌 투명하게 뻥 뚫려있는 것을 볼 수 있습니다. 이렇게 투명하게 뻥 뚫려있는 효과를 주기 위해서 Outcircle에
mask-image: radial-gradient(circle farthest-side at center, transparent 70%, whtie 50%);
설정을 해주었습니다.
이렇게 되면 원 가운데에 구멍이 하나 뚫리게 됩니다.
그리고 그 다음으로 Innercircle을 퍼센트만큼 채워주면 되는데요 이 부분에서 저는 background: conic-gradient 스타일을 활용했습니다. background: cornic-gradient는 내부에 색을 그라디언트로 채울 때 사용하는데, 특징은 원형으로(방사형으로) 색이 퍼진다는 겁니다. 이 특성을 이렇게도 이용할 수 있습니다.
1. 퍼센트만큼의 각도를 색으로 채운다.
2. 이외의 각도는 투명하게 처리한다.
이러면 데이터 값만큼 호가 그려집니다.
%를 사용해도 똑같은 효과를 얻겠지만 저는 deg가 편해 percent값을 deg로 변환하기 위해 3.6을 곱해주었습니다.
background: conic-gradient(
${props => `#16A799 0deg ${props.percent*3.6}deg`},
${props => `transparent ${props.percent*3.6}deg 360deg`}
);
그러면 이제 거의 다 완성되었습니다.
남은 부분은 안쪽의 텍스트 입니다.
import {
Container,
OutCircle,
InnerCircle,
Text,
} from './CircleChartStyles';
const CircleChart = ({size='30px', percent=0}) => {
return (
<Container
size={size}
>
<OutCircle size={size}>
<InnerCircle
size={size}
percent={percent}
/>
</OutCircle>
<Text>
{percent}%
</Text>
</Container>
)
}
export default CircleChart;
import styled from "styled-components";
export const Container = styled.div`
position: relative;
width: ${props => props.size};
height: ${props => props.size};
display: flex;
justify-content: center;
align-items: center;
`
export const Text = styled.div`
position: absolute;
font-size: 7px;
font-weight: 700;
`
export const OutCircle = styled.div`
border-radius: 50%;
background-color: #BCCCBF;
width: ${props => props.size};
height: ${props => props.size};
display: flex;
justify-content: center;
align-items: center;
mask-image: radial-gradient(circle farthest-side at center, transparent 70%, white 50%);
background-clip: text;
`
export const InnerCircle = styled.div`
width: ${props => props.size};
height: ${props => props.size};
background: conic-gradient(${props => `#16A799 0deg ${props.percent*3.6}deg`}, ${props => `transparent ${props.percent*3.6}deg 360deg`});
`
Text의 경우 Outcircle이 바깥에 위치하게 한 뒤 position: absolute를 이용해 위쪽에 띄우는 방식으로 설정했습니다.
이렇게 하는 이유는 Outcircle의 mask-image 값 때문에 내부에서는 텍스트가 보이지 않기 때문입니다.
'Front-End' 카테고리의 다른 글
[UI] Chakra와 Storybook 연동하기 (0) | 2023.04.17 |
---|---|
[DragNDrop] 드래깅 도중 hover 가상 선택자 문제 (0) | 2023.03.23 |
[DragNDrop] 드래그 도중 커서 변경 (0) | 2023.03.21 |
[DragNDrop] 드래그 도중 커서 변경 (0) | 2023.03.16 |
[Firebase] Auth undefined 문제 해결방법 (0) | 2023.03.15 |