안녕하세요.
지난 게시글에서 마우스 휠로 비디오의 볼륨을 조절하는 스크립트를 작성했었습니다.
이 스크립트를 실제 사용함에 있어 버그 수정과 기능 추가를 조금 더 해보겠습니다.
문제 1: Video의 객체를 찾지 못하는 문제 수정
Video가 재생되고 작성한 스크립트를 실행시키면 100% 이벤트가 연결되었습니다. 하지만, 일부 특정한 상황에서 Video 객체에 이벤트를 연결하지 못하는 문제가 사용하면서 발생이 되었습니다. 이 문제를 수정해 보도록 하겠습니다.
원인: 적용하려는 사이트가 SPA로 제공되어 새로고침 없이 동작한다
SPA(Single Page Application)으로 구성된 사이트가 왜 문제가 된 것인가?
해당 스크립트는 window.addEventListener("load", ...)를 통해 웹 페이지 load시에만 동작하게 구성이 되어있었습니다. 즉, 웹사이트 내에서 다른 페이지로 이동 시 동작이 되지 않게 됩니다.
해결 1: 웹페이지의 주소가 변경되는 이벤트를 활용한다
function urlChangeDetector() {
let lastUrl = location.href;
// URL 변경 확인 함수
function checkURLChange() {
if (lastUrl !== location.href) {
lastUrl = location.href;
// 주소가 변경되었을 때
console.log("URL changed!");
// 함수가 실행됩니다, observeForVideos 함수는 아래 블록 참조
setTimeout(observeForVideos, 1000); // 1초 후 실행 (필요에 따라 조정)
}
}
// 주기적으로 URL 변경 확인
setInterval(checkURLChange, 1000); // 1초마다 확인 (필요에 따라 조정)
// observeForVideos 함수구현은 아래의 블록 참조
observeForVideos();
}
window.addEventListener("load", urlChangeDetector);
위와 같은 소스를 적용하게 되면 웹 페이지에 최초 접속 후 urlChangeDetector()가 실행되며 setInterval을 통해 주기적으로 확인하게 됩니다.
코드 주석에도 작성하였지만 observeForVideos()는 다음 해결 2의 내용을 참고해 주세요
해결 2: Observer를 통해 Video 객체를 찾아 이벤트를 연결한다
디자인 패턴 중 Observer 패턴을 활용해 URL이 변경되었을 때, 새로운 Video Node를 찾습니다. 그 후 찾은 Video Node에 볼륨 조절 이벤트를 추가시킵니다.
function observeForVideos() {
const targetNode = document.body;
const config = { childList: true, subtree: true };
// 감시 이벤트
const observer = new MutationObserver((mutationsList, observer) => {
for (let mutation of mutationsList) {
if (mutation.type === "childList") {
mutation.addedNodes.forEach(node => {
// 비디오 객체를 찾고 이벤트 등록
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.tagName.toLowerCase() === "video") {
addVolumeControl(node);
}
}
});
}
}
});
// 감시 시작
observer.observe(targetNode, config);
console.log("Video observer started");
// 페이지 로드 시 이미 존재하는 비디오에 대해서도 처리
const existingVideos = document.querySelectorAll("video");
existingVideos.forEach(video => addVolumeControl(video));
}
위의 코드는 Observer가 페이지에 Load 되는 Element를 감시합니다. 감시하며 변경된 사항이 발견되면 지정된 기능이 실행됩니다.
Observer 디자인 패턴에 대한 자세한 내용은 이곳을 눌러 확인하세요!
실행 결과
웹 페이지가 Load 되고 페이지 내에서 이동시에도 새로운 Video에 대한 이벤트 등록이 정상적으로 등록되는 것을 확인했습니다.
문제 2: 볼륨이 변경된 것을 직관적으로 확인이 안 된다
내가 만든 코드고 어떻게 동작하는지 다 알고 있으니 확인하려면 Console.log를 사용해도 된다. 하지만 "직관적으로 변경될 때 데스크톱의 비디오 플레이어처럼 표시되게 하면 더 좋지 않을까?"라는 생각이 들게 되어 추가하게 되었습니다.
기능 1: 비디오의 볼륨을 화면에 표시하게 하자
function create() {
let element = document.getElementById("volumeDisplay");
if (!element) {
element = document.createElement("div");
element.id = "volumeDisplay";
element.style.cssText = `
position: absolute;
background-color: rgba(0,0,0,0.7);
color: white;
padding: 10px 20px;
border-radius: 20px;
font-size: 18px;
transition: opacity 0.3s ease-in-out;
z-index: 9999;
pointer-events: none;
opacity: 0;
`;
document.body.appendChild(element);
}
return element;
}
약간의 설명을 보태면 위의 Create함수는 volumeDisplay라는 id를 가진 div를 하나 생성해 검은색 배경의 약간의 투명도를 가지고 흰색으로 글자를 표시하게 만듭니다.
기능 2: 비디오의 중앙에 볼륨을 표시해 보자
function show(text, video) {
const display = create();
display.textContent = text;
// 비디오 요소의 위치와 크기 가져오기
const videoRect = video.getBoundingClientRect();
const videoWidth = videoRect.width;
const videoHeight = videoRect.height;
// 볼륨 표시 요소의 크기 계산
const displayRect = display.getBoundingClientRect();
const displayWidth = displayRect.width;
const displayHeight = displayRect.height;
// 볼륨 표시 요소의 위치 설정 (중앙)
display.style.left = `${videoRect.left + (videoWidth - displayWidth) / 2}px`;
display.style.top = `${videoRect.top + (videoHeight - displayHeight) / 2}px`;
// 표시 및 페이드 아웃
display.style.opacity = "1";
clearTimeout(display.fadeOutTimer);
display.fadeOutTimer = setTimeout(() => {
display.style.opacity = "0";
}, 2000);
}
Video의 화면 중앙으로 표시를 하기 위해서 Video 객체의 가로, 세로의 크기를 확인합니다. Display의 가로, 세로의 크기를 확인하여 처리하게 된 이유는 다음 단계에서 말씀드리도록 하겠습니다.
화면 중앙에 표시하기 위한 계산왼쪽으로부터 50%, 위에서부터 50%를 위치하게 되면 중앙에 기준점을 배치할 수 있습니다
기능 3: 창 크기를 조절했을 때에도 중앙에 표시되게 하자
위의 함수들로만 적용했을 때 문제가 없다고 생각이 들었지만, 화면의 창크기를 조절하니 이전의 중앙이라고 계산된 곳에 표시되는 것을 확인했습니다. 이 문제를 수정해 보도록 하겠습니다.
window.addEventListener("resize", () => {
const activeVideo = document.querySelector('video');
if (activeVideo) {
const volumePercentage = Math.round(activeVideo.volume * 100);
volumeDisplay.show(`Volume: ${volumePercentage}%`, activeVideo);
}
});
Event 항목 중 Resize 이벤트를 통해 창의 크기가 Resize가 될 때 화면에 볼륨을 표시하도록 수정하여 적용해 보았습니다. 아주 잘 적용되는 것을 확인했고 더 이상 문제가 없는 것으로 보입니다.
마무리
2개의 게시글을 통해 기획, 구현, 오류 조치, 기능 개선까지 진행해 보았습니다. 현재 마우스 휠을 통해 웹페이지에서 재생되는 비디오들의 볼륨을 조절하며 편하게 사용중에 있습니다. 위의 함수들을 잘 정리해서 Tampermonkey와 같은 Userscript를 사용할 수 있는 도구에 적용하면 잘 작동될 것입니다.
과정에서 어려움이 있거나 잘못된 내용, 추가하였으면 하는 내용 등 의견이 있으시면
언제든지 댓글로 알려주세요.
긴 글 읽어주셔서 감사합니다.
'Development > Javascript' 카테고리의 다른 글
[Javascript] DynamicsCompressorNode를 통해 Video의 소리를 풍부하게 만들기 (1) | 2024.09.26 |
---|---|
[Javascript] 마우스 휠로 비디오의 음량을 조절하기 - 1편 (0) | 2024.07.30 |