Implement drag'n'drop
This commit is contained in:
parent
63f5ddd8af
commit
266fe05c09
63
public/assets/scripts/FileSelector.js
Normal file
63
public/assets/scripts/FileSelector.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
export class FileSelector {
|
||||||
|
dropZones = [];
|
||||||
|
selectedFile = null;
|
||||||
|
onFileSelected = null;
|
||||||
|
|
||||||
|
constructor(mimeType) {
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
|
||||||
|
window.ondrop = (e) => e.preventDefault();
|
||||||
|
|
||||||
|
window.ondragover = (e) => {
|
||||||
|
if (![...e.dataTransfer.items].some(it => it.kind === "file")) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
if (!this.dropZones.some(dropZone => dropZone.contains(e.target))) {
|
||||||
|
e.dataTransfer.dropEffect = "none";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
selectFile(file) {
|
||||||
|
this.selectedFile = file;
|
||||||
|
|
||||||
|
if (this.onFileSelected) this.onFileSelected(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
addInput(elem) {
|
||||||
|
elem.oninput = () => {
|
||||||
|
setTimeout(() => this.selectFile(elem.files[0]), 200);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addDropZone(elem) {
|
||||||
|
this.dropZones.push(elem);
|
||||||
|
|
||||||
|
elem.ondrop = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const file = [...e.dataTransfer.files].find(file => file.type.startsWith(this.mimeType));
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
this.selectFile(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
elem.ondragover = (e) => {
|
||||||
|
const items = [...e.dataTransfer.items];
|
||||||
|
|
||||||
|
if (!items.some(it => it.kind === "file")) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (items.some(it => it.type.startsWith(this.mimeType))) {
|
||||||
|
e.dataTransfer.dropEffect = "copy";
|
||||||
|
elem.classList.add("dropping");
|
||||||
|
} else {
|
||||||
|
e.dataTransfer.dropEffect = "copy";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
elem.ondragleave = () => elem.classList.remove("dropping");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,21 +1,33 @@
|
|||||||
import { Notifier } from "./Notifier.js";
|
import { Notifier } from "./Notifier.js";
|
||||||
import { ProgressBar } from "./ProgressBar.js";
|
import { ProgressBar } from "./ProgressBar.js";
|
||||||
import { VideoCompressor } from "./VideoCompressor.js";
|
import { VideoCompressor } from "./VideoCompressor.js";
|
||||||
|
import { FileSelector } from "./FileSelector.js";
|
||||||
|
|
||||||
const notifier = new Notifier();
|
const notifier = new Notifier();
|
||||||
const progressBar = new ProgressBar();
|
const progressBar = new ProgressBar();
|
||||||
const videoCompressor = new VideoCompressor(progressBar);
|
const videoCompressor = new VideoCompressor(progressBar);
|
||||||
|
const fileSelector = new FileSelector("video/");
|
||||||
|
|
||||||
// Reset file input cache after reload
|
fileSelector.addInput(document.getElementById("file-input"));
|
||||||
document.getElementById("file-input").value = "";
|
fileSelector.addDropZone(document.getElementById("file-drop-area"));
|
||||||
|
|
||||||
|
fileSelector.onFileSelected = file => {
|
||||||
|
document.getElementById("uploaded-video").src = URL.createObjectURL(file);
|
||||||
|
document.getElementById("uploaded-video").load();
|
||||||
|
|
||||||
|
hideElements("#file-drop-area", "#file-input-spacing");
|
||||||
|
showElements("#uploaded-video", "#change-file");
|
||||||
|
};
|
||||||
|
|
||||||
async function compress(filesize, filesizeUnit) {
|
async function compress(filesize, filesizeUnit) {
|
||||||
document.getElementById("uploaded-video").pause();
|
document.getElementById("uploaded-video").pause();
|
||||||
|
|
||||||
const file = document.getElementById("file-input").files[0];
|
const sectionChangePromise = showSection("loading");
|
||||||
|
|
||||||
if (!file) {
|
if (!fileSelector.selectedFile) {
|
||||||
|
await sectionChangePromise; // Avoid changing sections at the same time
|
||||||
alert("Please select a file");
|
alert("Please select a file");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,12 +39,11 @@ async function compress(filesize, filesizeUnit) {
|
|||||||
case "mb": targetFilesize = filesize * 1000000; break;
|
case "mb": targetFilesize = filesize * 1000000; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
showSection("loading");
|
|
||||||
|
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
result = await videoCompressor.compress(file, targetFilesize, videoLength);
|
result = await videoCompressor.compress(fileSelector.selectedFile, targetFilesize, videoLength);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
await sectionChangePromise; // Avoid changing sections at the same time
|
||||||
alert(e.message);
|
alert(e.message);
|
||||||
|
|
||||||
showSection("file-picker");
|
showSection("file-picker");
|
||||||
@ -80,45 +91,16 @@ document.getElementById("compress").onclick = async () => {
|
|||||||
await compress(filesize, filesizeUnit);
|
await compress(filesize, filesizeUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const preset of document.getElementsByClassName("preset")) {
|
|
||||||
preset.onclick = () => compress(parseInt(preset.dataset.size), preset.dataset.unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("cancel").onclick = () => {
|
document.getElementById("cancel").onclick = () => {
|
||||||
videoCompressor.stop();
|
videoCompressor.stop();
|
||||||
|
|
||||||
showSection("file-picker");
|
showSection("file-picker");
|
||||||
};
|
};
|
||||||
|
|
||||||
function openFileSelector() {
|
|
||||||
document.getElementById("file-input").click();
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("file-drop-area").onclick = openFileSelector;
|
document.getElementById("change-file").onclick = () => document.getElementById("file-input").click();
|
||||||
document.getElementById("change-file").onclick = openFileSelector;
|
|
||||||
|
|
||||||
for (const tabbable of document.querySelectorAll("[tabindex='0']")) {
|
document.getElementById("back").onclick = () => showSection("file-picker");
|
||||||
tabbable.onkeydown = event => {
|
|
||||||
if (['Enter', 'Space'].includes(event.code))
|
|
||||||
tabbable.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("file-input").oninput = () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
const file = document.getElementById("file-input").files[0];
|
|
||||||
|
|
||||||
document.getElementById("uploaded-video").src = URL.createObjectURL(file);
|
|
||||||
document.getElementById("uploaded-video").load();
|
|
||||||
|
|
||||||
hideElements("#file-drop-area", "#file-input-spacing");
|
|
||||||
showElements("#uploaded-video", "#change-file");
|
|
||||||
}, 200);
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById("back").onclick = () => {
|
|
||||||
showSection("file-picker");
|
|
||||||
};
|
|
||||||
|
|
||||||
window.onbeforeunload = event => {
|
window.onbeforeunload = event => {
|
||||||
if (videoCompressor.inProgress) event.preventDefault();
|
if (videoCompressor.inProgress) event.preventDefault();
|
||||||
@ -130,3 +112,14 @@ function bytesToSizeString(bytes) {
|
|||||||
if (bytes >= 1000) return (bytes / 1000).toFixed(1) + " KB";
|
if (bytes >= 1000) return (bytes / 1000).toFixed(1) + " KB";
|
||||||
return bytes + " B";
|
return bytes + " B";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const tabbable of document.querySelectorAll("[tabindex='0']")) {
|
||||||
|
tabbable.onkeydown = event => {
|
||||||
|
if (['Enter', 'Space'].includes(event.code))
|
||||||
|
tabbable.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const preset of document.getElementsByClassName("preset")) {
|
||||||
|
preset.onclick = () => compress(parseInt(preset.dataset.size), preset.dataset.unit);
|
||||||
|
}
|
||||||
|
|||||||
@ -6,6 +6,8 @@ function showSection(section) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showElements(`#${section}-section`);
|
showElements(`#${section}-section`);
|
||||||
|
|
||||||
|
return new Promise(resolve => setTimeout(resolve, TRANSITION_TIME * 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideElements() {
|
function hideElements() {
|
||||||
|
|||||||
@ -29,6 +29,10 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#file-drop-area.dropping {
|
||||||
|
border-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
#uploaded-video {
|
#uploaded-video {
|
||||||
display: none;
|
display: none;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="description" content="Easily compress video files to a specific file size for uploading to various communication platforms that restrict upload size">
|
<meta name="description" content="Easily compress video files to a specific file size for uploading to various communication platforms that restrict upload size">
|
||||||
<title>Video Compressor</title>
|
<title>Compact.Video - Compress videos online</title>
|
||||||
<script defer src="/assets/scripts/ffmpeg/package/dist/umd/ffmpeg.js"></script>
|
<script defer src="/assets/scripts/ffmpeg/package/dist/umd/ffmpeg.js"></script>
|
||||||
<script defer src="/assets/scripts/ui.js"></script>
|
<script defer src="/assets/scripts/ui.js"></script>
|
||||||
<script defer type="module" src="/assets/scripts/main.js"></script>
|
<script defer type="module" src="/assets/scripts/main.js"></script>
|
||||||
@ -19,10 +19,10 @@
|
|||||||
<p>Compress video files to a specific file size, so you can upload them to your favorite social media and messaging apps!</p>
|
<p>Compress video files to a specific file size, so you can upload them to your favorite social media and messaging apps!</p>
|
||||||
|
|
||||||
<section id="file-picker-section">
|
<section id="file-picker-section">
|
||||||
<div id="file-drop-area" tabindex="0">
|
<label id="file-drop-area" tabindex="0">
|
||||||
<span>Drag and drop a file here!</span>
|
<span>Drag and drop a file here!</span>
|
||||||
</div>
|
|
||||||
<input id="file-input" type="file" accept="video/*" tabindex="-1">
|
<input id="file-input" type="file" accept="video/*" tabindex="-1">
|
||||||
|
</label>
|
||||||
<div id="file-input-spacing" style="margin-top: -1rem">
|
<div id="file-input-spacing" style="margin-top: -1rem">
|
||||||
<button class="simple">.</button>
|
<button class="simple">.</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user