html은 정말 간단하다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Picture in Picture</title>
<link rel="icon" type="image/png" href="favicon.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.10.2/css/all.min.css"/>
<link rel="stylesheet" href="style.css">
</head>
<body>
<video id="video" controls height="360" width="640" hidden></video>
<div class="button-container">
<button id="button">시작</button>
</div>
<script src="script.js"></script>
</body>
</html>
단순하게 PIP를 위한 video태그를 하나 만들어주고 버튼을 하나 만들어준다.
CSS도 역시 어려울 만한 것이 없다.
그래도 첫번째 줄부터 차례차례 알아보자.
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300&family=Poor+Story&family=Yeon+Sung&display=swap');
자신이 원하는 폰트의 url을 import해주어 글자의 폰트를 바꾸어준다.
html {
box-sizing: border-box;
}
나는 CSS에서 이 부분이 제일 중요하다고 생각한다.
쉽게 말하면, 자신이 원하는 치수대로 나오도록 box-sizing을 바꾸어주는 것이다.
이 부분의 자세한 설명은 밑에 링크를 통해 확인 바란다.
https://velog.io/@dldlswognqh/Box-Model
body {
margin: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: rgb(37, 37, 37);
}
첫번째!!!
body부분 CSS에서 가장 기본적으로 해주는 것인데,
일단 body 자체에 있는 margin을 없애고 본다.
두번째!!!
body의 height가 화면 전체의 높이를 차지하도록 100vh라는 값을 준다.
세번째!!!
나중에 생성하는 버튼이 가운데로 정렬되도록
display: flex를 주고 가로 세로도 가운데로 정렬해준다.
네번째!!!
배경색은 내가 원하는 색깔로~~
.button-container {
border: 2px solid black;
padding: 10px;
border-radius: 7px;
box-shadow: inset 0 20px 4px -19px rgba(255, 255, 255, 0.7);
}
첫번째!!!
border 설정도 취향대로!! 하지만 보기 좋게 설정
두번째!!!
보기 좋게 안쪽 여백을 준다.
세번째!!!
버튼이 속해있는 container 박스가 보기 좋게 border-radius를 줌
네번째!!!
조금 더 입체감을 위해서 box-shadow를 주는데
box-shadow : inset(선택) | offset-x | offset-y | blur-radius | spread-radius | color
inset : 요소가 움푹 들어간 것처럼 그림자가 요소의 테두리 안, 배경색 위, 내부 콘텐츠 밑에 그려짐
(사진 속 박스 위에 하얀색 부분)
offset-x 즉 수평 부분에는 그림자를 설정하지 않음
offset-y 즉 수직 거리에는 밑에쪽으로 20px
blur-radius : 크면 클 수록 그림자 테두리가 흐려지고 크기는 더 커지고 색은 더 밝아짐
spread-radius를 -19px로 음수로 주어 그림자가 줄어들도록
color는 내가 원하는 색!!!
button {
cursor: pointer;
outline: none;
width: 120px;
height: 75px;
font-family: 'Poor Story', cursive;
font-size: 25px;
color: white;
text-shadow: 0 2px 5px black;
background: linear-gradient(to top, #696969, #575757);
border: 2px solid black;
border-radius: 7px;
box-shadow: inset 0 20px 4px -19px rgba(255, 255, 255, 0.4), 0 12px 12px 0 rgba(0, 0, 0, 0.3);
}
첫번째!!!
cursor를 pointer로 설정하여 버튼에 가까이하면 손가락 모양으로!!
두번째!!!
outline: none으로 일반적으로 버튼 클릭하면 생기는 blue outline을 없애줌
세번째!!!
font-family로 아까 import했던 것을 사용하여준다.
네번째!!!
font-size는 내 기호에 따라 글자 크기를 설정한다.
다섯번째!!!
text-shadow: 0 2px 5px black;
글자에 형성되는 그림자를 수평으로는 0 아래로 2px blur는 5px로 설정
여섯번째!!!
linear-gradient로 선의 방향을 설정하고 두 가지 색을 설정해준다.
button:hover {
background: linear-gradient(to bottom, #696969, #575757);
}
button 위에 마우스가 있으면 linear-gradient를 변경해준다.
button:active {
transform: translateY(3px);
box-shadow: 0 6px 6px 0 rgba(0, 0, 0, 0.3);
}
버튼을 누르고 뗄 때까지 버튼을 3px 아래로 이동하고 그림자의 설정이 변하게 한다.
const videoElement = document.getElementById('video');
const button = document.getElementById('button');
video라는 id를 가져와서 videoElement라는 변수에 넣어줌.
button이라는 id를 가져와서 button이라는 변수에 넣어줌.
async function selectMediaStream() {
try {
const mediaStream = await navigator.mediaDevices.getDisplayMedia();
videoElement.srcObject = mediaStream;
videoElement.onloadedmetadata = () => {
videoElement.play();
}
} catch (error) {
console.log(error)
}
}
async/await을 통해 비동기 처리를 한다.
async/await을 사용하면 비동기 처리를 하지만 동기처럼 작성할 수 있어서 가독성이 좋아진다.
await 키워드를 사용하면 일반 비동기 처리처럼 바로 실행이 다음 라인으로 넘어가는 것이 아니라 결과값을 얻을 수 있을 때까지 기다려줌.
함수 선언부에서 function 앞에 async를 붙여서 작성해줌.
비동기/동기를 구분하지 않고 try/catch를 이용해 일관되게 예외 처리 가능.
navigator.mediaDevices.getDisplayMedia();를 통해 화면 기록을 할 수 있도록 해줌.
카메라의 mediaStream 은 videoElement
요소에 할당.
videoElement의 메타데이터가 로드될 때 videoElement가 실행되도록 함.
button.addEventListener('click', async () => {
button.disabled = true;
await videoElement.requestPictureInPicture();
button.disabled = false;
});
button.disabled = true : button이 클릭되었을 때 비활성화시킴
await videoElement.requestPictureInPicture() : PIP 시작되도록
button.disabled = false : 다시 리셋시킴
selectMediaStream();
selectMediaStream() 함수가 로드 되도록