😊 이번 시간에는 메뉴에서 활성화된 버튼을 중앙에 배치하는 효과를 만들어보았다!
1. 미리보기
See the Pen button center efffect by KumJungMin (@kumjungmin) on CodePen.
2. 코드 분석
1) html
- html을 보면 전체를 감싸는
.container
와.list
가 여러 개의.btn
을 감싸는 구조이다.
<div>
<h2 class="text">You cliked "<span class="result">?</span> "</h2>
</div>
<br/>
<!--메뉴 부분-->
<div class="container">
<div class="list">
<button class="btn" onclick="onClickBtn(this)"><span class="text">apple</span></button>
<button class="btn" onclick="onClickBtn(this)"><span class="text">pineapple</span></button>
<button class="btn" onclick="onClickBtn(this)"><span class="text">orange</span></button>
<button class="btn" onclick="onClickBtn(this)"><span class="text">pear</span></button>
<button class="btn" onclick="onClickBtn(this)"><span class="text">banana</span></button>
<button class="btn" onclick="onClickBtn(this)"><span class="text">tomato</span></button>
<button class="btn" onclick="onClickBtn(this)"><span class="text">orange</span></button>
<button class="btn" onclick="onClickBtn(this)"><span class="text">pear</span></button>
</div>
</div>
2) CSS(SCSS)
(1) body에 기본 스타일 지정하기
body
내부 요소들이 수직, 수평 중앙 정렬이 되도록flex
를 사용한다.
body {
height: 100vh; /* 수직중앙을 위해 높이 설정 필수 */
display: flex; /* 중앙배치 */
flex-direction: column;
align-items: center; /* 중앙배치 */
justify-content: center; /* 중앙배치 */
background-color: #010a01;
}
(2) list를 감싸는 container 요소
.container
는 스크롤이 발생하는.list
를 감싸는 역할을 한다.
- [1] 먼저,
.container
의 너비와 높이를 지정해준다. - [2] 특정 너비 이상인 경우에 자식요소(
.*list*
)가 벗어나지 않게overflow:hidden
을 해준다.
.container {
position: relative;
width: 100%; /* [1] */
max-width: 400px; /* [1] */
height: 50px; /* [1] */
overflow: hidden; /* [2] */
border: 1px solid #fff;
border-radius: 30px;
}
(3) btn을 감싸는 list 요소
.list
는.btn
을 감싸는 부모 클래스로, 스크롤이 발생하는 요소이다.
- [1] 스크롤이 발생할 수 있도록
overflow-x: auto
를 지정한다. - [2] 자식 클래스(
.btn
)이 수직 중앙이 되도록align-items: center
를 한다. - [3]
gap
을 사용해 자식 클래스 간에 간격을 준다.(flex로 설정해야 사용가능)
.list {
height: inherit;
overflow-x: auto; /* [1] */
display: flex; /* [2] */
align-items: center; /* [2] */
gap: 12px; /* [3] */
padding: 0 4px;
}
(4) btn 스타일 지정하기
.btn
에는 원하는 스타일을 지정하면 된다.
- 본인이 지정한 스타일은 아래와 같다.
.btn {
padding: 3px 17px;
font-size: 24px;
border-radius: 20px;
border: 1px solid #fff;
color: #fff;
background-color: #010a01;
cursor: pointer;
&.active {
background-color: red;
}
}
(3) 기타 스타일 설정하기
- 모든 글자에 네온 효과를 주기위해
.text
에text-shadow
를 지정했다.
.text {
color: #fff;
text-shadow:
0 0 7px #fff,
0 0 10px #fff,
0 0 21px #fff,
0 0 42px #0fa,
0 0 82px #0fa,
0 0 92px #0fa,
0 0 102px #0fa,
0 0 151px #0fa;
}
- 현재 클릭한 버튼의 내용을 나타나는
.result
에는 아래와 같이 스타일링했다.
.result {
font-size: 30px;
font-style: italic;
}
- 마지막으로 스크롤바가 보이지 않도록
scrollbar
에display: none
을 지정했다.
::-webkit-scrollbar {
display: none;
}
3) JS
(1) 버튼 중앙 배치 함수
- 클릭된 버튼을 중앙에 배치하기 위해서는 .container의 너비, 클릭된 .btn의 너비와 offsetLeft가 필요하다.
🤔 클릭된
.btn
의 offsetLeft값은 왜 필요할까?
- 우리는 아이템4를 클릭했고, 이 요소가
.container
를 기준으로 중앙에 위치해야 한다. - 먼저,
.list
를 기준으로 아이템4의offsetLeft
값을 알아야 한다. offsetLeft
값은 곧, 아이템4가container
시작 부분에 나타나기 위해 알아야할 스크롤 값과 같다.offsetLeft
값만으로scrollLeft
를 지정하면 아래와 같이.container
시작 부분에 배치된다.
👀
.container
, 클릭된.btn
의 너비는 왜 필요할까?
- 앞서 우리는
.btn
의offsetLeft
로scrollLeft
값을 지정했다. - 하지만 실제 우리가 원하는 건
.btn
이.container
를 기준으로 중앙에 위치하는 것이다. - 그러기 위해서는 아래 그림처럼
.container
의 너비의 반 만큼 스크롤을 우측으로 이동하고, 클릭한.btn
의 너비의 반만큼 좌측으로 이동해야한다.
💡 앞서 설명한 내용을 코드로 나타내면 아래와 같다.
const container = document.querySelector('.container');
const scrollDom = document.querySelector('.list');
const result = document.querySelector('.result');
function onClickBtn(target) {
const btnWidth = target.clientWidth; // 클릭한 버튼의 너비
const btnLeft = target.offsetLeft; // 클릭한 버튼의 offsetLeft
const center = container.clientWidth - btnWidth;
const left = btnLeft - center / 2;
scrollDom.scrollTo({ left, behavior: "smooth" });
}
(2) 마우스 휠 이벤트 변환 함수
- 마우스 휠을 위, 아래로 움직였을 때 메뉴의
scrollLeft
가 변화도록 해보았다. wheel
이벤트가 발생할 때마다 스크롤이 발생하는scrollDom
의scrollLeft
값을 변경해준다.
scrollDom.addEventListener('wheel', (e)=> {
e.preventDefault();
scrollDom.scrollLeft += e.deltaY;
})
(3) 버튼 active 클래스 추가 함수
- 클릭된 버튼에
active
클래스를 추가하여 별도 스타일링을 하고자 한다. - [1] 반복문을 이용해
btn
들에active
클래스가 있는지 체크하고 있다면 제거한다. - [2] 그 다음, 클릭된
target
에만active
클래스를 추가해준다.
const btns = document.querySelectorAll('.btn');
function addActiveClass(target) {
for (const btn of btns) {
// [1] 만약 btn에 active 클래스가 있다면 제거
const isActive = btn.classList.contains('active');
if(isActive) btn.classList.remove('active');
}
// [2] 현재 클릭한 target에 active 클래스를 추가
target.classList.add('active');
}
- 버튼이 클릭될 때마다
active
클래스를 지정해야하므로onClickBtn
에 해당 함수를 호출해주었다.
function onClickBtn(target) {
// ...
toggleActive(target); // 추가
}
(4) 클릭한 버튼의 text 나타내는 함수
- [1]
target
의 클래스가text
인 dom에 접근한다. - [2] 그 다음,
result
의 text를 변경해준다.
const result = document.querySelector('.result');
function changeResultText(target) {
const text = target.querySelector('.text'); // [1]
result.innerText = text.innerText; // [2]
}
- 버튼이 클릭될 때마다 동작해야하므로
onClickBtn
에 함수를 호출해주었다.
function onClickBtn(target) {
// ...
toggleActive(target);
changeResultText(target); // 추가
}
(5) 최종 코드
const container = document.querySelector('.container');
const scrollDom = document.querySelector('.list');
const result = document.querySelector('.result');
const btns = document.querySelectorAll('.btn');
// change btn position to center
function onClickBtn(target) {
const btnWidth = target.clientWidth;
const btnLeft = target.offsetLeft;
const center = container.clientWidth - btnWidth;
const left = btnLeft - center / 2;
scrollDom.scrollTo({ left, behavior: "smooth" });
toggleActive(target);
changeResultText(target);
}
function changeResultText(target) {
const text = target.querySelector('.text');
result.innerText = text.innerText;
}
// add active class on clicked button
function toggleActive(target) {
for (const btn of btns) {
const isActive = btn.classList.contains('active');
if(isActive) btn.classList.remove('active');
}
target.classList.add('active');
}
// change wheel to srollLeft
scrollDom.addEventListener('wheel', (e)=> {
e.preventDefault();
scrollDom.scrollLeft += e.deltaY;
})
💡 추가적으로
window
에서resize
가 발생한 경우에 대해서도 코드를 추가할 수 있을 것이다 🙂
반응형
'개발 기술 > css 애니메이션 (with js)' 카테고리의 다른 글
종이 글자 애니메이션 (with. clip-path) (2) | 2021.10.22 |
---|---|
버튼 커서 애니메이션 (with. js) (0) | 2021.10.20 |
svg로 gooey animation 만들기 (0) | 2021.10.18 |
wave 애니메이션(with. text-decoration) (0) | 2021.10.15 |
스크롤은 부드럽게, 글자에는 이미지를 넣어보자! (0) | 2021.10.13 |
댓글