반응형 무한 캐러셀 (모바일 이벤트 지원)
- 모바일 이벤트 지원 추가
- 스와이핑 모션 빠르기에 비례해 슬라이드 이동
- 자동 재생 지원
- 무한 캐러셀
- touch시 터치된 위치/시간 저장, autoplay일 경우 autoTimeout 삭제
- touch종료 시 터치된 지난 시간 , 이동한 위치, 속도를 이용해 대략적인 힘(force) 계산
- force에 비례해 넘길 슬라이드 개수(skips) 계산
- 개수만큼 슬라이드 이동 후, dx에 따라 슬라이드 방향 리셋
- autoplay 설정일 경우 autoplay 시작
onTouchstart = (e) => {
const { clientX, clientY } = e.touches[0];
this.touchStart = e.timeStamp;
this.touchPosition = { clientX, clientY };
if (this.interval) {
clearInterval(this.autoTimeout);
}
};
onTouchEnd = (e) => {
this.touchStartTimeout = console.log(e);
const { clientX, clientY } = e.changedTouches[0];
const timeStamp = e.timeStamp;
const dx = clientX - this.touchPosition.clientX;
const dy = clientY - this.touchPosition.clientY;
const dt = timeStamp - this.touchStart;
if (dx === 0) return;
const distance = Math.sqrt(dx ** 2 + dy ** 2);
const velocity = distance / dt;
const k = 10;
const force = velocity ** 2 * k;
let skips = Math.floor(Math.sqrt(force)) || 1;
while (skips) {
dx > 0 ? this.prevSlide() : this.nextSlide();
skips--;
}
this.direction = dx > 0 ? 'prev' : 'next';
if (this.interval) {
this.autoPlay();
}
};
모달 UI 분리
- 백드랍
import Component from '../components/Component.js';
export default class Backdrop extends Component {
constructor(target, attributes) {
super(target, 'div', {
...attributes,
className: 'Backdrop' + (attributes.className || ''),
styles: {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
position: 'fixed',
zIndex: 2000,
top: 0,
right: 0,
bottom: 0,
left: 0,
...attributes.styles,
},
});
}
}
- 모달
import Component from '../components/Component.js';
import Backdrop from './Backdrop.js';
export default class Modal extends Backdrop {
constructor(target, content, attributes) {
super(target, {
...attributes,
className: 'Modal ' + (attributes.className || ''),
styles: {
...attributes.styles,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
});
if (typeof content === 'string') {
this.addHTML(this.$, content);
} else if (typeof content === 'function') {
this.$content = new content(this.$);
}
this.bindEvents();
}
render() {
this.$content && this.$content.render && this.$content.render();
}
}
- 모달 사용하기
import Component from './Component.js';
import Modal from '../UI/Modal.js';
export default class CatInfoModal extends Modal {
constructor($parent, data) {
const { url, name, temperament, origin } = data;
super(
$parent,
`<div class="content-wrapper pd-5 card">
<div class="title mb-3">
<span>${name}</span>
<div class="close btn">x</div>
</div>
<div class="img-wrapper mb-3">
<img src="${url}" alt="${name}"/>
</div>
<div class="description">
<div>성격: ${temperament}</div>
<div>태생: ${origin}</div>
</div>
</div>`,
{
className: 'CatInfoModal',
tabIndex: 0,
styles: {
transition: 'opacity 300ms',
opacity: 0,
},
}
);
this.$.focus();
this.$.classList.add('fade-in');
this.data = data;
this.bindEvents();
}
removeWithFadeOut = () => {
this.$.classList.remove('fade-in');
this.$.classList.add('fade-out');
this.$.ontransitionend = () => this.$.remove();
};
onClick = (e) => {
if (e.target === this.$.querySelector('.close') || e.target === this.$) {
this.removeWithFadeOut();
}
};
onKeyDown = (e) => {
e.key === 'Escape' && this.removeWithFadeOut();
};
}