티스토리 뷰
프롤로그
며칠 전 프로젝트를 진행하다 리뷰를 남기는 컴포넌트에 평점을 구현해야 했다.
일상 속에서 리뷰를 남길땐 아무 생각없이 사용하던 것인데 막상 구현하려니 ?!하고 당황스러웠다.
열심히 구글링한 결과 JavaScript까지 동원한 엄청난 코드도 많았지만 최대한 간단하게 적용하고 싶었다.
지금 프론트엔드 기술스택은 Vue를 사용하고 있는데 npm package로 vue-star-rating이라는 것이 있었지만 왠만하면 패키지를 사용하고 싶지 않았다...
(사실 깔아서 적용시켜보려 했지만 mouse hover event가 안되서 다시 지웠다)
열심히 검색한 결과 원하는 대로 깔끔하게 구현된 코드가 있어 정리겸 글을 써본다 🧡
별점은 정적 구현과 동적 구현 두 가지가 있다.
정적 구현
사용자 리뷰나 평균 평점을 웹사이트에 보여주고 싶을 때 사용하는 코드이다.
실생활에서 볼 수 있는 예시로는 카카오맵이나 네이버 지도에서 음식점을 검색하면 나오는 바로 그 별점 창이다.
평균 평점은 소수점으로 계산되어 나오기 때문에 별을 %로 채워야 했다.
그런데 어떻게..? 😱😱
CSS에서 약간의 트릭을 적용하면 간단히 해결되었다. (이런 방법을 생각해내다니..!)
1. 우선 베이스가 될 별 모양을 배치한다.
2. 평점을 표시할 별모양을 똑같이 만든 후 display: absolute로 베이스 별을 덮어쓴다.
3. 그리고 별점이 보여지기를 원하는 만큼 width를 지정하면 된다.
html쪽 코드는 다음과 같다.
<div class="star-ratings">
<div
class="star-ratings-fill space-x-2 text-lg"
:style="{ width: ratingToPercent + '%' }"
>
<span>★</span><span>★</span><span>★</span><span>★</span><span>★</span>
</div>
<div class="star-ratings-base space-x-2 text-lg">
<span>★</span><span>★</span><span>★</span><span>★</span><span>★</span>
</div>
</div>
현재 Vue를 사용하고 있기 때문에 width 속성은 computed로 api로 넘어온 평균 평점 값을 계산하여 percentage로 변환하여 스타일 바인딩을 이용했다.
return값에 1.5를 더하여 주는 이유는 half star일 시 미세하게 절반이 안되어보여서 보여지는 값을 조정하기 위해 추가한 offset 값이다.
ratingToPercent() {
const score = +this.restaurant.averageScore * 20;
return score + 1.5;
}
CSS는 다음과 같이 주었다. space-x-2, text-lg는 tailwind CSS의 클래스이다.
만약 채워진 별을 구현하고 싶다면 -webkit-text-fill, webkit-text-stroke 관련된 속성은 지우면 된다.
webkit 관련 코드는 기존의 색상을 override하기 때문에 stroke를 주고 싶다면 fill color도 같이 구현해야 한다!!
base가 별점의 뼈대를 담당, fill이 채워졌을 때의 별점 스타일이다.
.star-ratings {
color: #aaa9a9;
position: relative;
unicode-bidi: bidi-override;
width: max-content;
-webkit-text-fill-color: transparent; /* Will override color (regardless of order) */
-webkit-text-stroke-width: 1.3px;
-webkit-text-stroke-color: #2b2a29;
}
.star-ratings-fill {
color: #fff58c;
padding: 0;
position: absolute;
z-index: 1;
display: flex;
top: 0;
left: 0;
overflow: hidden;
-webkit-text-fill-color: gold;
}
.star-ratings-base {
z-index: 0;
padding: 0;
}
실제 웹사이트에 구현된 모습은 다음과 같다.
border 속성을 주는 디자인으로 밀고가고 있는 웹사이트인데 예쁘게 잘 구현된 것 같다.
동적 구현
이 방식은 사용자가 유동적으로 별점 주기를 클릭할 수 있고 사용자가 클릭한 값은 반영되게 하는 시스템이다.
솔직히 정적 구현은 어떻게든 할 수 있겠는데 동적 구현은 어떻게 하는거지...?!?! 하는 생각이 들 수 있다.
이 역시 html과 CSS만을 이용하여 구현할 수 있다.
앞서 정적 구현에선 base를 덮어씌운 방법과 달리 flex와 CSS의 general sibling selector (~)를 이용한다.
~ 선택자란 해당 태그와 같은 레벨에 있는 태그를 전부 선택하는 것이다.
예를 들어 코드가 이렇게 주어진다면 스타일에서 div~p는 div 이후의 동일 레벨에 있는 p를 전부 선택하는 것이기 때문에 아래와 같은 결과가 나타나게 된다.
동적 별점도 이 원리를 사용한다.
우리에게 실제로 보여지는건 1~5 순이기 때문에 flex-direction: row-reverse로 순서를 뒤집어 준다.
이제 hover 속성과 checked되었을 때 ~를 걸면 우리가 원하는 동적 별점을 만들 수 있다.
코드는 다음과 같다.
<div class="star-rating space-x-4 mx-auto">
<input type="radio" id="5-stars" name="rating" value="5" v-model="ratings"/>
<label for="5-stars" class="star pr-4">★</label>
<input type="radio" id="4-stars" name="rating" value="4" v-model="ratings"/>
<label for="4-stars" class="star">★</label>
<input type="radio" id="3-stars" name="rating" value="3" v-model="ratings"/>
<label for="3-stars" class="star">★</label>
<input type="radio" id="2-stars" name="rating" value="2" v-model="ratings"/>
<label for="2-stars" class="star">★</label>
<input type="radio" id="1-star" name="rating" value="1" v-model="ratings" />
<label for="1-star" class="star">★</label>
</div>
사용자가 선택한 별점 값을 반영해야 하므로 v-model="ratings"로 바인딩을 해줬다.
CSS는 아래와 같다.
checked 된 상태일 때 별을 좀 더 진하게 했다.
.star-rating {
display: flex;
flex-direction: row-reverse;
font-size: 2.25rem;
line-height: 2.5rem;
justify-content: space-around;
padding: 0 0.2em;
text-align: center;
width: 5em;
}
.star-rating input {
display: none;
}
.star-rating label {
-webkit-text-fill-color: transparent; /* Will override color (regardless of order) */
-webkit-text-stroke-width: 2.3px;
-webkit-text-stroke-color: #2b2a29;
cursor: pointer;
}
.star-rating :checked ~ label {
-webkit-text-fill-color: gold;
}
.star-rating label:hover,
.star-rating label:hover ~ label {
-webkit-text-fill-color: #fff58c;
}
실제 웹사이트에 구현된 모습이다.
별점 옆에 이모지를 넣어 별점에 따라 다르게 나타나도록 잔재주(?)를 부려봤다.
이모지는 v-model로 바인딩 된 값을 switch문으로 필터링 해 computed로 구현하면 된다.
참고
정적 별점 코드는 하도 많이 찾으러 다녀서 출처를 찾을 수 없다 ㅠㅠ...
아래는 동적 별점 코드이다!
- Total
- Today
- Yesterday
- 다익스트라
- BigInteger
- form
- 그래프
- CustomHook
- 브루트포스
- 백트래킹
- 이분탐색
- BFS
- 백준
- swea
- 문자열
- web
- 삼성역테기출
- 알고리즘
- matches
- dfs
- 벨만포드
- 프로그래머스
- 우선순위큐
- REACT
- 시뮬레이션
- java
- Validation
- 해시
- regex
- 구현
- 정규식
- vue.js
- dp
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |