이전에 했던 가상 키보드 보다는 쉬웠다.
원래는 강의만 보고 따라하면 1시간도 걸리지 않는 프로젝트인데, 최대한 안 보고 기능만 똑같이 구현하도록 노력했다.
그래서 좀 시간이 걸렸다. 갑자기 든 생각이 내가 개발하는 방식은 먼저 어떻게 기능을 구현할지에 대해서 떠올릴 때까지 멍 때리면서 생각을 한다... 그리고 생각을 바탕으로 검색을 하거나 직접 구현을 해본다. 이게 맞는 것일까..?
그래도 이런식으로 반복하면 시간은 꽤 걸리지만 기분은 좋다. 내가 스스로 짠 코드고, 그에 맞는 코드를 찾아서 적용시켜서 부드럽게 돌아가는 프로그램을 만들었으니...
어쨌든 내가 생각했을 때는 최대한 깔끔하게 작성한 코드인 것 같다.
설명은 다 해놨으니 모르는게 있다면 댓글 달아주세요.
// src/js/ImageSlider.js
import ScheduleManager from './scheduleManager';
export default class ImageSlider {
#schduler;
#currentPosition;
#sliderWrap;
#slider;
#autoBtn;
#indicatorWrap;
#nextBtn;
#prevBtn;
#slideWidth;
#positionMax;
constructor() {
this.#currentPosition = 0;
this.#schduler = new ScheduleManager(
this.#intervalClickEvent.bind(this),
2.5,
);
this.#autoSlider(true);
this.assignElement();
this.addEvent();
}
assignElement() {
this.#sliderWrap = document.querySelector('.slider-wrap');
this.#slider = document.getElementById('slider');
this.#autoBtn = document.getElementById('control-wrap');
this.#indicatorWrap = this.#sliderWrap.querySelector('.indicator-wrap ul');
this.#nextBtn = this.#sliderWrap.querySelector('#next');
this.#prevBtn = this.#sliderWrap.querySelector('#previous');
this.#slideWidth = this.#slider.clientWidth;
this.#positionMax = this.#slider.childElementCount;
}
// 이벤트를 연결해주는 함수
addEvent() {
this.#autoBtn.addEventListener('click', this.onClickAutoBtn.bind(this));
this.#nextBtn.addEventListener('click', this.nextSlide.bind(this));
this.#prevBtn.addEventListener('click', this.previousSlide.bind(this));
[...this.#indicatorWrap.children].forEach(indicator =>
indicator.addEventListener('click', this.#onClickIndicatorBtn.bind(this)),
);
}
// 현재 위치에서 1을 더해주는 함수
#addPosition() {
this.#currentPosition++;
if (this.#currentPosition >= this.#positionMax) {
this.#currentPosition = 0;
return;
}
}
// 현재 위치에서 1을 빼주는 함수
#subtractPosition() {
this.#currentPosition--;
if (this.#currentPosition < 0) {
this.#currentPosition = this.#positionMax - 1;
return;
}
}
// 시작, 정지 버튼에 관련한 이벤트 함수
onClickAutoBtn(event) {
const controlWrap = event.target.closest('div');
const isAuto = !controlWrap.classList.toggle('view');
this.#autoSlider(isAuto);
}
// 이미지가 자동으로 슬라이딩 되는지에 대한 여부를 판별하는 함수
// 해당 함수는 생성자 함수가 실행될 때 같이 실행이 되야 한다.
// 그 이유는 페이지에 처음 들어갔을 때 자동으로 실행이 되어야 하기 때문이다.
#autoSlider(isAuto) {
if (isAuto) {
this.#schduler.begin();
} else {
this.#schduler.end();
}
}
// 일정 시간마다 클릭이벤트가 자동으로 호출되는 함수
// 최대한 기능이 중복되지 않도록 따로 함수를 분리했고,
#intervalClickEvent() {
const mouseClick = new MouseEvent('click', {
view: window,
});
this.#nextBtn.dispatchEvent(mouseClick);
}
// indicator 관련 함수
// 클릭 및 자동으로 넘어가는 기능에 필요한 함수
#updateIndicator() {
// indicator의 전체를 구한 다음 active 효과를 삭제
// 그 다음 현재 위치에 맞는 요소에 대한 active 효과를 적용
const listNodes = this.#indicatorWrap.children;
[...listNodes].forEach(list => list.classList.remove('active'));
listNodes[this.#currentPosition].classList.add('active');
// 스타일 설정
this.#slider.style.left = `-${this.#currentPosition * this.#slideWidth}px`;
}
// indicator를 클릭했을 때 직접 발생되는 이벤트 함수
#onClickIndicatorBtn(event) {
// html에 설정된 data 속성의 값을 가져온 뒤 현재 위치값으로 덮어쓴다.
const targetIndex = event.target.dataset.index;
this.#currentPosition = targetIndex;
this.#updateIndicator();
// 이 함수를 실행한 이유는 자동으로 이미지가 넘어가고 있는 도중, 시간이
// 별로 남지 않았을 때 누르게 되면 클릭한 순간에 이미지가 바로 넘어가기 때문에
// 타이머를 리셋하고 다시 시작하는 방식으로 구현
this.#schduler.reset();
}
// 다음 버튼을 누르면 다음으로 넘어가는 함수
nextSlide() {
this.#addPosition();
this.#updateIndicator();
}
// 이전 버튼을 누르면 이전으로 넘어가는 함수
previousSlide() {
this.#subtractPosition();
this.#updateIndicator();
}
}
다음은 scheduleManager인데, ImageSlider에서 좀 더 편하게 사용할 수 있도록 따로 만들었다. 딱히 특별한 부분은 없다.
export default class ScheduleManager {
#timerId;
#func;
#second;
constructor(intervalFunc, second) {
this.#timerId = 0;
this.#func = intervalFunc;
this.#second = second;
}
begin() {
this.#timerId = setInterval(this.#func, this.#second * 1000);
}
end() {
clearInterval(this.#timerId);
}
reset() {
this.end();
this.begin();
}
}
다음은 HTML 파일이다. 강의에서 제공해준 것과 거의 비슷하지만 좀 더 편하게 구현하기 위해서 indicator 부분에 data 속성을 추가했다.
<!DOCTYPE html>
<html>
<head> </head>
<body>
<div class="slider-wrap" id="slider-wrap">
<ul class="list slider" id="slider">
<li>
<img src="<%= require('./src/image/red.jpeg') %>" />
</li>
<li>
<img src="<%= require('./src/image/orange.jpeg') %>" />
</li>
<li>
<img src="<%= require('./src/image/yellow.jpeg') %>" />
</li>
<li>
<img src="<%= require('./src/image/green.jpeg') %>" />
</li>
<li>
<img src="<%= require('./src/image/blue.jpeg') %>" />
</li>
<li>
<img src="<%= require('./src/image/indigo.jpeg') %>" />
</li>
<li>
<img src="<%= require('./src/image/violet.jpeg') %>" />
</li>
</ul>
<div class="btn next" id="next"><i class="fa fa-arrow-right"></i></div>
<div class="btn previous" id="previous">
<i class="fa fa-arrow-left"></i>
</div>
<div class="indicator-wrap" id="indicator-wrap">
<ul>
<li class="active" data-index="0"></li>
<li data-index="1"></li>
<li data-index="2"></li>
<li data-index="3"></li>
<li data-index="4"></li>
<li data-index="5"></li>
<li data-index="6"></li>
</ul>
</div>
<div class="control-wrap" id="control-wrap">
<i class="fa fa-pause" id="pause" data-status="pause"></i>
<i class="fa fa-play" id="play" data-status="play"></i>
</div>
</div>
</body>
</html>
다음은 CSS파일이다. 그냥 보면서 HTML요소를 어떻게 배치시켰는지에 대해서만 보면 끝난다.
CSS를 공부하면서 헷갈리는 것 중에 하나가 위치인데, 다음에 한 번 제대로 공부 해야겠다.
@import url('~@fortawesome/fontawesome-free/css/all.min.css');
* {
margin: 0;
padding: 0;
list-style: none;
}
/* ! */
.slider-wrap {
width: 1000px;
height: 400px;
margin: 50px auto;
position: relative;
overflow: hidden;
}
/* ! */
.slider-wrap ul.slider {
left: 0;
display: flex;
width: 100%;
height: 100%;
position: absolute;
}
/* ! */
.slider-wrap ul.slider li {
float: left;
width: 1000px;
height: 400px;
}
/* ! */
.btn {
position: absolute;
width: 50px;
height: 60px;
top: 50%;
margin-top: -25px;
line-height: 57px;
text-align: center;
cursor: pointer;
background: rgba(0, 0, 0, 0.1);
z-index: 100;
user-select: none;
transition: 0.1s;
}
.btn:hover {
background: rgba(0, 0, 0, 0.3);
}
/* ! */
.next {
right: -50px;
border-radius: 7px 0px 0px 7px;
color: white;
}
/* ! */
.previous {
left: -50px;
border-radius: 0px 7px 7px 7px;
color: white;
}
/* ! */
.slider-wrap:hover .next {
right: 0px;
}
/* ! */
.slider-wrap:hover .previous {
left: 0px;
}
.indicator-wrap {
height: 15px;
position: relative;
text-align: center;
min-width: 20px;
margin-top: 350px;
margin-left: auto;
margin-right: auto;
}
.indicator-wrap ul {
width: 100%;
}
.indicator-wrap ul li {
border-radius: 50%;
background: #fff;
opacity: 0.5;
position: relative;
top: 0;
cursor: pointer;
margin: 0 4px;
display: inline-block;
width: 15px;
height: 15px;
}
.indicator-wrap ul li.active {
width: 15px;
height: 15px;
opacity: 1;
}
.slider-wrap ul {
transition: 0.4s;
}
.control-wrap {
top: 350px;
right: 35px;
width: auto;
position: absolute;
}
.control-wrap i {
color: white;
cursor: pointer;
}
.fa-play {
display: none;
}
.fa-pause {
display: block;
}
.view .fa-play {
display: block;
}
.view .fa-pause {
display: none;
}
코드는 여기까지이다.
'개발 > 프로젝트' 카테고리의 다른 글
Project 1 : 가상키보드(4) - 개발 완료 (0) | 2022.08.26 |
---|---|
Project 1 : 가상키보드(3) - 개발 중간 완료 (0) | 2022.08.22 |
Project 1 : 가상키보드(2) - 개발 진행 상황 (0) | 2022.08.22 |
Project 1 : 가상키보드(1) - 개발 환경 설정 (0) | 2022.08.19 |