마우스 오버 후 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 { state } from './state.js';
|
||||||
import * as pinchZoom from './handlers/pinch-zoom.js';
|
import * as pinchZoom from './handlers/pinch-zoom.js';
|
||||||
import * as selection from './handlers/selection.js';
|
import * as selection from './handlers/selection.js';
|
||||||
import * as dragDrop from './handlers/drag-drop.js';
|
import * as dragDrop from './handlers/drag-drop.js';
|
||||||
|
import * as hoverPreview from './handlers/hover-preview.js';
|
||||||
|
|
||||||
// ── DOM ──────────────────────────────────────────────
|
// ── DOM ──────────────────────────────────────────────
|
||||||
const grid = document.getElementById('grid');
|
const grid = document.getElementById('grid');
|
||||||
@@ -38,6 +39,7 @@ const callbacks = {
|
|||||||
pinchZoom.init({ gridContainer, thumbSizeHint });
|
pinchZoom.init({ gridContainer, thumbSizeHint });
|
||||||
selection.init({ gridContainer, rubberBand, grid, state, callbacks });
|
selection.init({ gridContainer, rubberBand, grid, state, callbacks });
|
||||||
dragDrop.init({ grid, gridContainer, dragGhostEl, state, callbacks });
|
dragDrop.init({ grid, gridContainer, dragGhostEl, state, callbacks });
|
||||||
|
hoverPreview.init();
|
||||||
|
|
||||||
// ── 버튼 이벤트 ──────────────────────────────────
|
// ── 버튼 이벤트 ──────────────────────────────────
|
||||||
inputPrefix.addEventListener('input', updatePreview);
|
inputPrefix.addEventListener('input', updatePreview);
|
||||||
@@ -146,6 +148,7 @@ function createCard(file, idx, dataUrl) {
|
|||||||
card.appendChild(body);
|
card.appendChild(body);
|
||||||
|
|
||||||
dragDrop.setupCard(card);
|
dragDrop.setupCard(card);
|
||||||
|
hoverPreview.setupCard(card);
|
||||||
card.addEventListener('click', selection.onCardClick);
|
card.addEventListener('click', selection.onCardClick);
|
||||||
|
|
||||||
return card;
|
return card;
|
||||||
|
|||||||
@@ -269,6 +269,29 @@ body {
|
|||||||
@keyframes spin { to { transform: rotate(360deg); } }
|
@keyframes spin { to { transform: rotate(360deg); } }
|
||||||
#loading p { color: var(--text); font-size: 14px; }
|
#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 {
|
#toast {
|
||||||
position: fixed; bottom: 40px; left: 50%;
|
position: fixed; bottom: 40px; left: 50%;
|
||||||
|
|||||||
Reference in New Issue
Block a user