<div id="wrapper">
<div class="import-files">
<div id="drop-area">
<label for="file-selector">
<div>
<span>Drag'n </span>
<span class="drop">drop your photos</span>
<span>or</span>
</div>
<div>
<span class="fake-link">browse...</span>
</div>
<input type="file" id="file-selector" multiple accept="image/*" />
</div>
</label>
</div>
</div>
<div id="processing">Processing...</div>
<p id="view">
</p>
<template>
<div class="photo" style="background-image:url('${item.thumbnailBase64}')" >
<h3>${item.name}</h3>
<div>
${item.size4human}
</div>
<div>
<span>
${item.width}x${item.height}
</span>
<small>
resized to ${item.widthReduced}x${item.heightReduced}
</small>
</div>
<button type="button" onclick="this.parentNode.remove()">❌</button>
</div>
</template>
</div>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
background: black;
height: 100vh;
padding: 16px 16px 32px 16px;
}
#processing{
display: none;
color : white;
text-align: center;
}
.import-files {
background-color: white;
padding: 16px;
border-radius: 20px;
}
#drop-area {
border-radius: 20px;
padding: 0;
transition: 0.3s;
> label {
cursor: pointer;
border: 2px dashed grey;
min-height: 64px;
border-radius: 20px;
padding: 12px;
display: flex;
flex-direction: column;
font-size: 32px;
position: relative;
@media (min-width: 600px) {
flex-direction: row;
font-size: 48px;
}
//span {
align-items: center;
text-align: center;
justify-content: center;
transition: 0.3s;
transform: scale(1);
background-image: linear-gradient(
90deg,
#ffeb3b,
#9c27b0 41.07%,
#e91e63 76.05%,
#ff7a18
);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: none;
font-weight: bold;
//}
span {
transition: 0;
}
.fake-link {
color: blue;
}
&:hover,
&:focus {
border-color: blue;
.fake-link {
text-decoration: underline;
}
}
input {
display: none;
}
}
&.drop-active {
box-shadow: inset 0 0 16px grey;
> label {
//transform: scale(1.025);
transition: 0.15s;
border-style: solid;
}
span:not(.drop) {
opacity: 0;
}
}
}
.mt-1 {
margin-top: 1em;
}
.photo {
background-size: cover;
background-repeat: no-repeat;
background-position: center;
width: 300px;
height: 200px;
border-radius: 16px;
display: inline-flex;
flex-flow: column;
justify-content: space-between;
align-items: start;
vertical-align: top;
margin: 0 8px 8px 0;
border: 2px solid gold;
padding: 8px;
box-sizing: border-box;
position: relative;
h3 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
word-break: break-all;
}
* {
background: rgb(255 255 255 / 50%);
color: black;
backdrop-filter: blur(5px);
padding: 5px;
border-radius: 16px;
display: inline-block;
}
button {
position: absolute;
top: 0;
right: 0;
}
}
View Compiled
window.onload = function () {
const fileSelector = document.getElementById("file-selector");
const wrapper = document.body;
const dropArea = document.getElementById("drop-area");
//let timer = null;
wrapper?.addEventListener("dragover", (event) => {
event.stopPropagation();
event.preventDefault();
dropArea.classList.add("drop-active");
// Style the drag-and-drop as a "copy file" operation.
if (event && event.dataTransfer) {
event.dataTransfer.dropEffect = "copy";
}
});
wrapper?.addEventListener("dragleave", (event) => {
//clearTimeout(timer);
// timer = setTimeout(() => {
dropArea.classList.remove("drop-active");
// }, 1000);
});
wrapper?.addEventListener("drop", (event) => {
event.stopPropagation();
event.preventDefault();
dropArea.classList.remove("drop-active");
if (event && event.dataTransfer) {
getFiles((event.dataTransfer.files as unknown) as File[]);
}
});
if (window.FileList && window.File && fileSelector) {
fileSelector.addEventListener("change", async (event: any) => {
getFiles(event.target.files);
});
}
};
function formatFileSize(bytes: number = 0): string {
const units = ["Octets", "Ko", "Mo", "Go", "To", "Po"];
// https://stackoverflow.com/a/20732091
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + " " + units[i];
}
interface FileExtended extends File {
thumbnailBase64: string;
loading: boolean;
width: number;
height: number;
widthReduced: number;
heightReduced: number;
}
async function getFiles(files: File[]) {
document.getElementById("processing").style.display = "block";
for (const file of files) {
if (file.type.startsWith("image")) {
const item: FileExtended = file;
item.size4human = formatFileSize(file.size);
item.loading = true;
const { resized, original }: any = await getThumbnailImg(item, 450, 300);
item.thumbnailBase64 = resized;
item.loading = false;
item.original = original;
const template = document.querySelector("template").innerHTML;
document.querySelector("#view").innerHTML += eval("`" + template + "`");
}
}
document.getElementById("processing").style.display = "none";
}
// To display a thumbnail
async function getThumbnailImg(
file: FileExtended,
maxWidth: number = 100,
maxHeight: number = 100
): Promise<{ resized: string; original: string }> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function (e) {
const img = document.createElement("img");
img.src = e.target?.result as string;
img.onload = function (e2) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
ctx?.drawImage(img, 0, 0);
file.width = img.width;
file.height = img.height;
canvas.width = img.width;
canvas.height = img.height;
// calculate the new width / height respecting the original ratio
if (img.width > img.height) {
if (img.width > maxWidth) {
canvas.height *= maxWidth / img.width;
canvas.width = maxWidth;
}
} else {
if (img.height > maxHeight) {
canvas.width *= maxHeight / img.height;
canvas.height = maxHeight;
}
}
file.widthReduced = canvas.width;
file.heightReduced = canvas.height;
// resize
const ctx2 = canvas.getContext("2d");
ctx2?.drawImage(img, 0, 0, canvas.width, canvas.height);
const dataurl = canvas.toDataURL(file.type);
resolve({ resized: dataurl, original: e.target?.result });
};
img.onerror = (error) => {
reject(error);
};
};
reader.onerror = (error) => {
reject(error);
};
reader.readAsDataURL(file);
});
}
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.