컨트롤 바
비디오 제어
광고 기능
프로젝트 전체 컴포넌트 구조
player.tsx - 부모 컴포넌트
Video.tsx 바로가기 - 자식 컴포넌트
Controls.tsx 바로가기 - 자식 컴포넌트
비디오 태그의 이벤트 핸들러 처리
const Video = () => {
const controllerRef = useRef<ControllerInterface>(null);
const containerRef = useRef<HTMLDivElement>(null);
const videoRef = useRef<HTMLVideoElement>(null);
const srcRef = useRef<HTMLSourceElement>(null);
// video source 링크
const srcOrigin =
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
const srcAd =
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4";
// container Props & handlers
const containerProps = {
ref: containerRef,
tabIndex: 0,
onKeyDown: (e: React.KeyboardEvent) => {
if (controllerRef.current) controllerRef.current.handleKeyDown(e);
},
onMouseEnter: () => {
if (controllerRef.current) controllerRef.current.handleMouseIn();
},
onMouseLeave: () => {
if (controllerRef.current) controllerRef.current.handleMouseLeave();
},
onMouseMove: (e: React.MouseEvent) => {
if (controllerRef.current) controllerRef.current.handleMouseMove(e);
},
};
// video Prop & handlers
const videoProps = {
ref: videoRef,
width: "100%",
controls: false,
onTimeUpdate: () => {
if (controllerRef.current) controllerRef.current.handleTimeUpdate();
},
onClick: () => {
if (controllerRef.current) controllerRef.current.handleVideoClick();
},
};
// Controls Props
const controlProps = {
ref: controllerRef,
containerRef: containerRef,
videoRef: videoRef,
srcRef: srcRef,
srcOrigin: srcOrigin,
srcAd: srcAd,
};
return (
<Layout>
<Container {...containerProps}>
<VideoWrapper {...videoProps}>
<source ref={srcRef} src={srcOrigin} type="video/mp4" />
</VideoWrapper>
<Controls {...controlProps} />
</Container>
</Layout>
);
};
export default Video;
const Container = styled.div`
position: relative;
&:focus {
border: none;
outline: none;
}
`;
const VideoWrapper = styled.video`
&::-webkit-media-controls {
display: none !important;
}
`;
Video 컴포넌트의 자식 컴포넌트인 Controls
핸들러 함수들과 useImperativeHandle 함수로 부모컴포넌트의 ref에 함수 올리기
// 비디오 재생 키보드 이벤트 핸들러
const handleKeyDown = (e: React.KeyboardEvent): void => {
switch (e.code) {
case "ArrowLeft":
videoElement!.currentTime -= 5;
break;
case "ArrowRight":
videoElement!.currentTime += 5;
break;
case "Space":
if (videoElement!.paused) {
videoElement!.play();
setIsPlaying(true);
} else {
videoElement!.pause();
setIsPlaying(false);
}
break;
default:
return;
}
};
// 비디오 클릭 시 재생/정지 핸들러
const handleVideoClick = () => {
if (videoElement) {
if (videoElement.paused) {
videoElement.play();
setIsPlaying(true);
} else {
videoElement.pause();
setIsPlaying(false);
}
}
};
// mouse event handlers - 마우스가 비디오 위에서 움직일 때 컨트롤바 보이게.
const handleMouseMove = (e: React.MouseEvent) => {
setShowControl(true);
setHideCursor(false);
setCoords({ x: e.screenX });
};
const handleMouseIn = () => {
setShowControl(true);
};
const handleMouseLeave = () => {
setShowControl(false);
};
// 동영상 시간 업데이트 핸들러
const handleTimeUpdate = () => {
setCurrent(videoElement?.currentTime || 0);
};
// 마우스 3초 이상 호버 시 컨트롤 바 안보이도록 타임아웃 useEffect
useEffect(() => {
const timeOut = setTimeout(() => {
setShowControl(false);
setHideCursor(true);
}, 3000);
return () => clearTimeout(timeOut);
}, [coords]);
// volume toggle에 현재 volume 업데이트 useEffect
useEffect(() => {
if (volumeRef && volumeRef.current) {
volumeRef.current.value = String(volume);
}
}, [isVolume]);
// 부모 컴포넌트로 핸들러 함수 올리기
useImperativeHandle(ref, () => ({
handleVideoClick,
handleKeyDown,
handleMouseIn,
handleMouseLeave,
handleMouseMove,
handleTimeUpdate,
}));
광고 기능
///// 광고 기능 /////
// 30초 이후 광고 불러오도록 트리거
useEffect(() => {
if (isAdPlayed) return;
if (30 < current) {
setAdTime({ ...adTime, originTime: current + 5, adLoaded: true });
}
}, [current]);
// 광고 안내 문구 마운트 & 5초 후 광고 로드
useEffect(() => {
if (srcElement && videoElement) {
setAdTime({ ...adTime, adPopUp: true });
let countdown = setInterval(
() => setAdCountDown((prev) => prev - 1),
1000
);
setTimeout(() => {
setAdTime({ ...adTime, adPopUp: false });
srcElement!.src = srcAd;
videoElement!.load();
videoElement!.play();
clearInterval(countdown);
}, 5000);
}
}, [adTime.adLoaded]);
// 광고 종료 이후 기존 비디오의 위치로 돌아가기
useEffect(() => {
if (isAdPlayed) return;
if (srcElement?.src === srcAd && current === totalTime) {
srcElement!.src = srcOrigin;
videoElement!.load();
videoElement!.currentTime = adTime.originTime;
videoElement!.play();
setIsAdPlayed(true);
}
}, [current]);