[251119] feat: FloatingGradientLayer..Experimental..3

🕐 커밋 시간: 2025. 11. 19. 19:56:11

📊 변경 통계:
  • 총 파일: 2개
  • 추가: +95줄
  • 삭제: -27줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx

🔧 주요 변경 내용:
  • UI 컴포넌트 아키텍처 개선
  • 소규모 기능 개선
This commit is contained in:
2025-11-19 19:56:12 +09:00
parent 276ee65979
commit d5336b4322
2 changed files with 95 additions and 27 deletions

View File

@@ -148,24 +148,16 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
}; };
}, [dispatch]); }, [dispatch]);
// ✅ DOM 렌더링 후 그라데이션 배경 숨기기 - HomePanel→DetailPanel 전환 완료 시 // ✅ DetailPanelBackground 이미지 완전 렌더링 후 그라데이션 배경 숨기기
useEffect(() => { const handleBackgroundImageReady = useCallback(() => {
// DOM이 렌더링된 후 약간의 지연 시간을 두고 그라데이션 숨김 console.log('[TRACE-GRADIENT] ✅ DetailPanel - BackgroundImage fully rendered, hiding gradient');
const timer = setTimeout(() => { dispatch(updateHomeInfo({
dispatch(updateHomeInfo({ name: panel_names.HOME_PANEL,
name: panel_names.HOME_PANEL, panelInfo: {
panelInfo: { showGradientBackground: false,
showGradientBackground: false, }
} }));
})); }, [dispatch]);
console.log('[TRACE-GRADIENT] 🔴 DetailPanel mounted 100ms timeout set showGradientBackground: false');
}, 100); // 100ms 지연으로 DOM 렌더링 완료 후 실행
return () => {
clearTimeout(timer); // 컴포넌트 언마운트 시 타이머 정리
console.log('[TRACE-GRADIENT] 🔴 DetailPanel unmount - gradient timer cleared');
};
}, [dispatch]); // dispatch 포함
// ✅ [251118] DetailPanel이 사라질 때 HomePanel 활성화 // ✅ [251118] DetailPanel이 사라질 때 HomePanel 활성화
useEffect(() => { useEffect(() => {
@@ -805,8 +797,12 @@ export default function DetailPanel({ panelInfo, isOnTop, spotlightId }) {
return ( return (
<div ref={containerRef}> <div ref={containerRef}>
<DetailPanelBackground launchedFromPlayer={panelLaunchedFromPlayer} patnrId={panelPatnrId}/> <DetailPanelBackground
launchedFromPlayer={panelLaunchedFromPlayer}
patnrId={panelPatnrId}
onImageReady={handleBackgroundImageReady}
/>
<TPanel <TPanel
isTabActivated={false} isTabActivated={false}

View File

@@ -1,7 +1,9 @@
// src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx // src/views/DetailPanel/components/DetailPanelBackground/DetailPanelBackground.jsx
import React, { import React, {
useCallback,
useEffect, useEffect,
useMemo, useMemo,
useRef,
useState, useState,
} from 'react'; } from 'react';
@@ -25,9 +27,11 @@ import css from './DetailPanelBackground.module.less';
* - true: PlayerPanel의 MEDIA 재생 완료 후 진입 (updatePanel로 전달됨) * - true: PlayerPanel의 MEDIA 재생 완료 후 진입 (updatePanel로 전달됨)
* - false/undefined: 다른 패널(Shop Now, You May Like 등)에서 진입 * - false/undefined: 다른 패널(Shop Now, You May Like 등)에서 진입
* - 이 값에 따라 배경 UI를 다르게 표시할 수 있음 * - 이 값에 따라 배경 UI를 다르게 표시할 수 있음
* @param {Function} onImageReady - 이미지가 DOM에 완전히 렌더링되었을 때 호출되는 콜백
*/ */
export default function DetailPanelBackground({ launchedFromPlayer = false, patnrId }) { export default function DetailPanelBackground({ launchedFromPlayer = false, patnrId, onImageReady }) {
const [imageReady, setImageReady] = useState(false); const [imageReady, setImageReady] = useState(false);
const imgRef = useRef(null);
const BG_MAP = { const BG_MAP = {
1: qvc, 1: qvc,
@@ -43,10 +47,44 @@ export default function DetailPanelBackground({ launchedFromPlayer = false, patn
return BG_MAP[patnrId] || qvc; return BG_MAP[patnrId] || qvc;
}, [patnrId]); }, [patnrId]);
// ✅ [251119] 프리로드된 이미지 사용 로직 수정 // ✅ 이미지가 DOM에 완전히 렌더링되었는지 확인하는 함수
// launchedFromPlayer와 상관없이 항상 배경 이미지를 표시하도록 수정 const checkImageFullyLoaded = useCallback(() => {
const img = imgRef.current;
if (!img) return false;
// 이미지 로드 완료 및 실제 크기가 파싱됨
const isLoaded = img.complete && img.naturalWidth > 0;
// 렌더링된 DOM에 크기가 설정됨
const isRendered = img.offsetHeight > 0 && img.offsetWidth > 0;
const isDOMReady = isLoaded && isRendered;
if (isDOMReady) {
console.log('[DetailPanelBackground] ✅ 이미지 DOM 완전 렌더링 완료:', {
imgSrc: detailPanelBg,
complete: img.complete,
naturalWidth: img.naturalWidth,
naturalHeight: img.naturalHeight,
offsetHeight: img.offsetHeight,
offsetWidth: img.offsetWidth,
});
}
return isDOMReady;
}, [detailPanelBg]);
// 🔧 [251119] PlayerPanel에서 올라온 DetailPanel은 배경 이미지를 표시하지 않음
// launchedFromPlayer가 false일 때만 배경 이미지를 로드하고 표시
useEffect(() => { useEffect(() => {
// 이미지가 프리로드되었는지 확인 // launchedFromPlayer가 true이면 배경 이미지를 로드하지 않음 (PlayerPanel 비디오 보이도록)
if (launchedFromPlayer) {
console.log('[DetailPanelBackground] Skip background image loading - launchedFromPlayer=true (showing PlayerPanel video)');
setImageReady(false);
return;
}
// launchedFromPlayer가 false일 때만 배경 이미지 로드
console.log('[DetailPanelBackground] Loading background image - launchedFromPlayer=false');
if (ImagePreloader.isLoaded(detailPanelBg)) { if (ImagePreloader.isLoaded(detailPanelBg)) {
console.log('[DetailPanelBackground] Using preloaded image:', detailPanelBg); console.log('[DetailPanelBackground] Using preloaded image:', detailPanelBg);
setImageReady(true); setImageReady(true);
@@ -64,7 +102,40 @@ export default function DetailPanelBackground({ launchedFromPlayer = false, patn
setImageReady(true); setImageReady(true);
}); });
} }
}, [detailPanelBg]); // launchedFromPlayer 제거 }, [detailPanelBg, launchedFromPlayer]); // launchedFromPlayer 추가
// ✅ 이미지가 DOM에 완전히 렌더링되면 부모에 콜백 호출
useEffect(() => {
if (!imageReady || !imgRef.current) return;
// 이미 렌더링된 경우 (캐시된 이미지)
if (checkImageFullyLoaded()) {
if (onImageReady) {
onImageReady();
}
return;
}
// 아직 렌더링 중인 경우: onLoad 대기
const img = imgRef.current;
const handleLoad = () => {
if (checkImageFullyLoaded()) {
if (onImageReady) {
onImageReady();
}
}
};
// img 요소가 ready 상태라면 즉시 실행
if (img.complete) {
handleLoad();
} else {
img.addEventListener('load', handleLoad);
return () => {
img.removeEventListener('load', handleLoad);
};
}
}, [imageReady, onImageReady, checkImageFullyLoaded]);
useEffect(() => { useEffect(() => {
console.log('[DetailPanelBackground] 배경 이미지 경로:', detailPanelBg); console.log('[DetailPanelBackground] 배경 이미지 경로:', detailPanelBg);
@@ -84,9 +155,10 @@ export default function DetailPanelBackground({ launchedFromPlayer = false, patn
/> />
)} )}
{/* 실제 배경 이미지 - 항상 표시되도록 수정 */} {/* 실제 배경 이미지 - launchedFromPlayer가 false일 때만 표시 */}
{imageReady && ( {imageReady && !launchedFromPlayer && (
<img <img
ref={imgRef}
src={detailPanelBg} src={detailPanelBg}
alt="" alt=""
className={css.backgroundImage} className={css.backgroundImage}