[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:
@@ -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}
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user