마우스 오버 후 1초가 지나면 이미지 미리보기 팝업창 실행

This commit is contained in:
2026-05-14 11:43:54 +09:00
parent 8c90aa5be8
commit c09227facc
3 changed files with 103 additions and 4 deletions
+73
View File
@@ -0,0 +1,73 @@
const DELAY = 1000;
let _el = null;
let _imgEl = null;
let _timer = null;
let _visible = false;
export function init() {
_el = document.createElement('div');
_el.className = 'hover-preview';
_imgEl = document.createElement('img');
_el.appendChild(_imgEl);
document.body.appendChild(_el);
}
export function setupCard(card) {
card.addEventListener('mouseenter', _onEnter);
card.addEventListener('mouseleave', _onLeave);
}
function _onEnter() {
const card = this;
clearTimeout(_timer);
_timer = setTimeout(() => {
const thumb = card.querySelector('img.card-thumb');
if (!thumb) return;
_imgEl.src = thumb.src;
_visible = true;
_show(card);
}, DELAY);
}
function _onLeave() {
clearTimeout(_timer);
_timer = null;
_visible = false;
_el.style.display = 'none';
_el.style.visibility = 'hidden';
}
function _show(card) {
// 위치 계산 전 화면 밖에 렌더링해서 실제 크기를 측정
_el.style.display = 'block';
_el.style.visibility = 'hidden';
_el.style.left = '0px';
_el.style.top = '0px';
requestAnimationFrame(() => {
if (!_visible) return;
const popup = _el.getBoundingClientRect();
const cardRect = card.getBoundingClientRect();
const vw = window.innerWidth;
const vh = window.innerHeight;
const gap = 16;
// 가로: 카드 오른쪽 우선, 공간 부족 시 왼쪽
let left = cardRect.right + gap;
if (left + popup.width > vw - 8) {
left = cardRect.left - popup.width - gap;
}
left = Math.max(8, Math.min(left, vw - popup.width - 8));
// 세로: 카드와 중앙 정렬, 화면 경계 클램프
let top = cardRect.top + (cardRect.height - popup.height) / 2;
top = Math.max(8, Math.min(top, vh - popup.height - 8));
_el.style.left = left + 'px';
_el.style.top = top + 'px';
_el.style.visibility = 'visible';
});
}