마우스 오버 후 1초가 지나면 이미지 미리보기 팝업창 실행
This commit is contained in:
@@ -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';
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { state } from './state.js';
|
||||
import * as pinchZoom from './handlers/pinch-zoom.js';
|
||||
import * as selection from './handlers/selection.js';
|
||||
import * as dragDrop from './handlers/drag-drop.js';
|
||||
import { state } from './state.js';
|
||||
import * as pinchZoom from './handlers/pinch-zoom.js';
|
||||
import * as selection from './handlers/selection.js';
|
||||
import * as dragDrop from './handlers/drag-drop.js';
|
||||
import * as hoverPreview from './handlers/hover-preview.js';
|
||||
|
||||
// ── DOM ──────────────────────────────────────────────
|
||||
const grid = document.getElementById('grid');
|
||||
@@ -38,6 +39,7 @@ const callbacks = {
|
||||
pinchZoom.init({ gridContainer, thumbSizeHint });
|
||||
selection.init({ gridContainer, rubberBand, grid, state, callbacks });
|
||||
dragDrop.init({ grid, gridContainer, dragGhostEl, state, callbacks });
|
||||
hoverPreview.init();
|
||||
|
||||
// ── 버튼 이벤트 ──────────────────────────────────
|
||||
inputPrefix.addEventListener('input', updatePreview);
|
||||
@@ -146,6 +148,7 @@ function createCard(file, idx, dataUrl) {
|
||||
card.appendChild(body);
|
||||
|
||||
dragDrop.setupCard(card);
|
||||
hoverPreview.setupCard(card);
|
||||
card.addEventListener('click', selection.onCardClick);
|
||||
|
||||
return card;
|
||||
|
||||
@@ -269,6 +269,29 @@ body {
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
#loading p { color: var(--text); font-size: 14px; }
|
||||
|
||||
/* ── 호버 이미지 팝업 ── */
|
||||
.hover-preview {
|
||||
position: fixed;
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
z-index: 8000;
|
||||
pointer-events: none;
|
||||
background: #0d0d1a;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.85), 0 0 0 1px rgba(124,106,247,0.15);
|
||||
}
|
||||
.hover-preview img {
|
||||
display: block;
|
||||
max-width: 840px;
|
||||
max-height: 840px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border-radius: 6px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* ── 토스트 ── */
|
||||
#toast {
|
||||
position: fixed; bottom: 40px; left: 50%;
|
||||
|
||||
Reference in New Issue
Block a user