마우스 오버시 로딩바 애니메이션 추가
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
const DELAY = 1000;
|
||||
|
||||
let _el = null;
|
||||
let _imgEl = null;
|
||||
let _timer = null;
|
||||
let _visible = false;
|
||||
const SVG_NS = 'http://www.w3.org/2000/svg';
|
||||
const RADIUS = 20;
|
||||
const CIRC = 2 * Math.PI * RADIUS; // ~125.66
|
||||
|
||||
let _el = null;
|
||||
let _imgEl = null;
|
||||
let _timer = null;
|
||||
let _visible = false;
|
||||
let _loaderEl = null;
|
||||
|
||||
export function init() {
|
||||
_el = document.createElement('div');
|
||||
@@ -22,7 +27,10 @@ export function setupCard(card) {
|
||||
function _onEnter() {
|
||||
const card = this;
|
||||
clearTimeout(_timer);
|
||||
_showLoader(card);
|
||||
|
||||
_timer = setTimeout(() => {
|
||||
_removeLoader();
|
||||
const thumb = card.querySelector('img.card-thumb');
|
||||
if (!thumb) return;
|
||||
_imgEl.src = thumb.src;
|
||||
@@ -35,12 +43,60 @@ function _onLeave() {
|
||||
clearTimeout(_timer);
|
||||
_timer = null;
|
||||
_visible = false;
|
||||
_el.style.display = 'none';
|
||||
_el.style.visibility = 'hidden';
|
||||
_removeLoader();
|
||||
_el.style.display = 'none';
|
||||
_el.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
// ── 원형 로딩바 ──────────────────────────────────
|
||||
|
||||
function _showLoader(card) {
|
||||
_removeLoader();
|
||||
|
||||
const svg = document.createElementNS(SVG_NS, 'svg');
|
||||
svg.classList.add('card-hover-loader');
|
||||
svg.setAttribute('viewBox', '0 0 48 48');
|
||||
|
||||
// 배경 원
|
||||
const bg = document.createElementNS(SVG_NS, 'circle');
|
||||
bg.classList.add('loader-bg');
|
||||
bg.setAttribute('cx', '24');
|
||||
bg.setAttribute('cy', '24');
|
||||
bg.setAttribute('r', '24');
|
||||
|
||||
// 트랙 링
|
||||
const track = document.createElementNS(SVG_NS, 'circle');
|
||||
track.classList.add('loader-track');
|
||||
track.setAttribute('cx', '24');
|
||||
track.setAttribute('cy', '24');
|
||||
track.setAttribute('r', String(RADIUS));
|
||||
|
||||
// 진행 호
|
||||
const progress = document.createElementNS(SVG_NS, 'circle');
|
||||
progress.classList.add('loader-progress');
|
||||
progress.setAttribute('cx', '24');
|
||||
progress.setAttribute('cy', '24');
|
||||
progress.setAttribute('r', String(RADIUS));
|
||||
progress.style.strokeDasharray = CIRC;
|
||||
progress.style.strokeDashoffset = CIRC;
|
||||
|
||||
svg.appendChild(bg);
|
||||
svg.appendChild(track);
|
||||
svg.appendChild(progress);
|
||||
card.appendChild(svg);
|
||||
_loaderEl = svg;
|
||||
}
|
||||
|
||||
function _removeLoader() {
|
||||
if (_loaderEl) {
|
||||
_loaderEl.remove();
|
||||
_loaderEl = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 이미지 팝업 ──────────────────────────────────
|
||||
|
||||
function _show(card) {
|
||||
// 위치 계산 전 화면 밖에 렌더링해서 실제 크기를 측정
|
||||
_el.style.display = 'block';
|
||||
_el.style.visibility = 'hidden';
|
||||
_el.style.left = '0px';
|
||||
@@ -55,14 +111,12 @@ function _show(card) {
|
||||
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));
|
||||
|
||||
|
||||
@@ -269,6 +269,40 @@ body {
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
#loading p { color: var(--text); font-size: 14px; }
|
||||
|
||||
/* ── 카드 호버 원형 로딩바 ── */
|
||||
.card-hover-loader {
|
||||
position: absolute;
|
||||
top: calc(var(--thumb-size) / 2);
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
overflow: visible;
|
||||
}
|
||||
.card-hover-loader .loader-bg {
|
||||
fill: rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
.card-hover-loader .loader-track {
|
||||
fill: none;
|
||||
stroke: rgba(255, 255, 255, 0.15);
|
||||
stroke-width: 3;
|
||||
}
|
||||
.card-hover-loader .loader-progress {
|
||||
fill: none;
|
||||
stroke: var(--accent);
|
||||
stroke-width: 3;
|
||||
stroke-linecap: round;
|
||||
transform-origin: 24px 24px;
|
||||
transform: rotate(-90deg);
|
||||
animation: hover-loader-fill 1s linear forwards;
|
||||
}
|
||||
@keyframes hover-loader-fill {
|
||||
from { stroke-dashoffset: 125.66; }
|
||||
to { stroke-dashoffset: 0; }
|
||||
}
|
||||
|
||||
/* ── 호버 이미지 팝업 ── */
|
||||
.hover-preview {
|
||||
position: fixed;
|
||||
|
||||
Reference in New Issue
Block a user