2023 전국 B모듈 첫 번째 동영상 편집 및 미리보기 개발 구현
주요 기능은
- 동영상 스냅샷, 속성값 저장, 드래그 앤 드롭, 편집 미리보기 등이 있다
해당 문제를 풀기 위해 알아야 할 지식
해당 문제를 풀기 위해 알아야 할 JS 문법은
기본 VIDEO API 구조 및 드래그 앤 드롭에 대한 이해도가 있어야 하며
비디오마다 속성들을 저장하고 활용해야 한다면 클래스나 객체를 이용해야 한다
메인 비디오의 currentBar를 이동할 때 JS의 버블링과 dispatchEvent를 알아야
유연하게 개발할 수 있다.
JS 개발 코드
const file = document.querySelector("#file");
const video = document.querySelector("#video");
const nowPage = document.querySelector("#nowPage");
const endPage = document.querySelector("#endPage");
const nowTime = document.querySelector("#nowTime");
const endTime = document.querySelector("#endTime");
const title = document.querySelector("#title");
const videoNav = document.querySelector("#videoNav");
const editVideo = document.querySelector("#editVideo");
const snapshotNav = document.querySelector("#snapshotNav");
const snapSelect = document.querySelector("#snapSelect");
const currentBar = document.querySelector("#currentBar");
const videoEditWrap = document.querySelector("#videoEditWrap");
const videosWrap = document.querySelector("#videosWrap");
const leftV = document.querySelector("#left");
const rightV = document.querySelector("#right");
const btns = document.querySelectorAll(".btns");
let Data = [], page = 1, snapshot = 10;
let selected = false;
currentBar.addEventListener("mousedown",(e)=>{
selected = true;
})
addEventListener("mouseup",(e)=>{
selected = false;
})
currentBar.addEventListener("mousemove",(e)=>{
e.stopPropagation();
videoEditWrap.dispatchEvent(new MouseEvent("mousemove",e))
})
btns.forEach(element => {
element.addEventListener("mousemove",(e)=>{
e.stopPropagation();
videoEditWrap.dispatchEvent(new MouseEvent("mousemove",e))
})
});
btns.forEach(element => {
element.addEventListener("click",(e)=>{
e.stopPropagation();
// videoEditWrap.dispatchEvent(new MouseEvent("click",e))
})
});
currentBar.addEventListener("click",(e)=>{
e.stopPropagation();
videoEditWrap.dispatchEvent(new MouseEvent("click",e))
})
videoEditWrap.addEventListener("mousemove",(e)=>{
if(selected && e.offsetX <= 1000) {
currentBar.style.left = e.offsetX+"px";
currentBar.className = e.offsetX;
video.currentTime = e.offsetX / 1000 * video.duration;
nowTime.textContent = timeFormat(Math.ceil(video.currentTime));
Data[page-1].currentBar = e.offsetX+"px";
Data[page-1].currentTimes = Math.ceil( video.currentTime);
localStorage.setItem("filePath", JSON.stringify(Data));
}
})
videoEditWrap.addEventListener("click",(e)=>{
currentBar.style.left = e.offsetX+"px";
currentBar.className = e.offsetX;
video.currentTime = e.offsetX / 1000 * video.duration;
nowTime.textContent = timeFormat(Math.ceil(video.currentTime));
Data[page-1].currentBar = e.offsetX+"px";
Data[page-1].currentTimes = Math.ceil(video.currentTime);
localStorage.setItem("filePath", JSON.stringify(Data));
})
addEventListener("keydown",(e) => {
if(e.key == "f"){
file.click();
}
if(e.key == "i"){
if((Number(currentBar.className) + Number(rightV.className)) <= 1000){
leftV.style.width = currentBar.className+"px";
leftV.className = (currentBar.className);
Data[page-1].left = (currentBar.className);
Data[page-1].start = (currentBar.className) / 1000 * video.duration;
editVideo.currentTime = (currentBar.className) / 1000 * video.duration;
localStorage.setItem("filePath", JSON.stringify(Data));
}
}
if(e.key == "o"){
if((Number(leftV.className) + Number(1000-currentBar.className)) <= 1000){
rightV.style.width = (1000-currentBar.className)+"px";
rightV.className = (1000-currentBar.className);
Data[page-1].right = (1000-currentBar.className);
Data[page-1].end = (currentBar.className) / 1000 * video.duration;
localStorage.setItem("filePath", JSON.stringify(Data));
}
}
if(e.key == "r") {
leftV.className = "0";
rightV.className = "0";
leftV.style.width = "0px";
rightV.style.width = "0px";
Data[page-1].left = (currentBar.className);
Data[page-1].right = (1000-currentBar.className);
Data[page-1].start = 0;
Data[page-1].end = video.duration;
localStorage.setItem("filePath", JSON.stringify(Data));
}
if(e.key == "d"){
let small = [];
for(let i = 0; i < Data.length; i++){
if(i != page - 1){
small.push(Data[i])
}
}
if(page > small.length){
page--;
}
console.log(small);
Data = small;
videoDraw();
}
if(e.keyCode == 32){ // 스페이스바
if(editVideo.paused && Data[page-1].end != 0){
editVideo.play();
}
else {
editVideo.pause();
}
}
})
file.addEventListener("change",()=>{
for(let i = 0; i < file.files.length; i++) {
file.files[i].left = 0;
file.files[i].right = 0;
file.files[i].currentBar = "0px";
file.files[i].currentTimes = 0;
file.files[i].start = 0;
file.files[i].end = 0;
Data.push(file.files[i])
Data[i].names = file.files[i].name;
console.log(Data);
}
for(let i = 0; i < file.files.length; i++) {
if(localStorage.getItem("filePath") != null){
let small = JSON.parse(localStorage.getItem("filePath"));
small.forEach(element => {
if(element.names == file.files[i].name){
Data[i].left = element.left;
Data[i].right = element.right;
Data[i].currentBar = element.currentBar;
Data[i].currentTimes = element.currentTimes;
Data[i].start = element.start;
Data[i].end = element.end;
Data[i].names = element.names;
}
});
}
}
videoNav.style.display = "flex";
videoDraw();
})
snapSelect.addEventListener("change",(e)=>{
snapshot = e.target.value;
snapDraw();
})
function snapDraw() {
snapshotNav.innerHTML = "";
let time = video.duration / (snapshot - 1)
for(let i = 0; i < snapshot; i++){
const videos = document.createElement("video");
videos.style.width = 90 / snapshot +"%";
videos.src = URL.createObjectURL(Data[page-1]);
videos.currentTime = time * i;
videos.addEventListener("click",(e)=>{
video.currentTime = time * i;
currentBar.style.left = (time * i) / video.duration * 1000 +"px";
currentBar.className = (time * i) / video.duration * 1000;
e.stopPropagation();
nowTime.textContent = timeFormat(Math.ceil(video.currentTime));
Data[page-1].currentBar = (time * i) / video.duration * 100+"%";
Data[page-1].currentTimes = Math.ceil(time * i);
})
videos.addEventListener("mousemove",(e)=>{
e.stopPropagation();
videoEditWrap.dispatchEvent(new MouseEvent("mousemove",e))
})
snapshotNav.appendChild(videos);
}
}
function left() {
if(page != 1) {
page--;
videoDraw()
}
}
function right() {
if(page != Data.length){
page++;
videoDraw();
}
}
function timeFormat(n) {
let hour = Math.floor(n / 3600);
let minute = Math.floor(n % 3600 / 60);
let second = n % 60;
if(hour < 10){
hour = "0"+hour;
}
if(minute < 10){
minute = "0"+minute;
}
if(second < 10) {
second = "0"+second;
}
return `${hour}:${minute}:${second}`;
}
function videoDraw() {
endPage.textContent = Data.length;
videosWrap.innerHTML = "";
video.src = URL.createObjectURL(Data[page-1]);
editVideo.src = URL.createObjectURL(Data[page-1]);
nowPage.textContent = page;
console.log(Data[page-1].currentTimes);
nowTime.textContent = timeFormat(Data[page-1].currentTimes);
title.textContent = Data[page-1].names;
currentBar.style.left = Data[page-1].currentBar;
currentBar.className = (Data[page-1].currentBar).replace(/[^0-9]/g,'');
leftV.className = Data[page-1].left
rightV.className = Data[page-1].right
editVideo.currentTime = Data[page-1].start;
if(Data[page-1].end == -1) {
setTimeout(() => {
Data[page-1].end = video.duration;
}, 100);
}
setTimeout(() => {
endTime.textContent = timeFormat(Math.ceil(video.duration));
snapDraw();
}, 100);
leftV.style.width = Data[page-1].left+"px";
rightV.style.width = Data[page-1].right+"px";
let temp, start, end;
for(let i = 0; i < Data.length; i++){
let videos = document.createElement("video");
videos.src = URL.createObjectURL(Data[i]);
videos.classList.add("videos");
videos.id = i + 1;
videos.draggable = true;
videos.addEventListener("dragstart",(e)=>{
start = e.target.id - 1;
});
videos.addEventListener("dragover", (e) => {
e.preventDefault();
});
videos.addEventListener("drop",(e)=>{
e.preventDefault();
end = e.target.id - 1;
if(page -1 == start){
page = end+1;
}
else if(page -1 == end){
page = start+1;
}
temp = Data[start];
Data[start] = Data[end];
Data[end] = temp;
videoDraw();
})
if(i == page-1) {
videos.classList.add("red");
}
setTimeout(() => {
videos.currentTime = videos.duration / 9;
videosWrap.appendChild(videos);
}, 100);
}
setTimeout(() => {
const videos = document.querySelectorAll(".videos");
videos.forEach(element => {
element.addEventListener("click",()=>{
videos.forEach(element => {
element.classList.remove("red");
});
page = element.id;
videoDraw();
element.classList.add("red");
})
});
}, 100);
localStorage.setItem("filePath", JSON.stringify(Data));
}
editVideo.addEventListener("timeupdate",()=>{
if(editVideo.currentTime >= Data[page-1].end && Data[page-1].end != 0){
editVideo.pause();
editVideo.currentTime = 0;
}
})
'개발 > 웹개발' 카테고리의 다른 글
요리사가 되고 싶어서 PHP로 만개의 레시피를 만들어보자. (1) | 2024.04.20 |
---|---|
움직이는 시외버스 시간표를 JS로 만들어보자 (0) | 2024.04.20 |
타자 연습 웹서비스를 JS로 만들어보자.. (0) | 2024.04.20 |
다각형 차트를 수학과 JS로 만들어보자.. (0) | 2024.04.17 |
천안을 소개하는 홈페이지를 html로 만들어보자.. (0) | 2024.03.16 |