Trang chủ
Upload UI
Save
Settings
Sign Up
Log In
Save
Settings
HTML
Copy
<div class="up-img" id="upImg"> <div class="box"> <div class="inner"> <div class="init"> <img src="https://i.ibb.co/5cQkzZN/img-upload.png" alt="img" /> <h3>Kéo thả hoặc click để tải ảnh lên</h3> </div> </div> <input type="file" id="imgFile" name="imgFile" accept="image/*" hidden /> <div class="prog"> <h4>Đang tải... <span>0%</span></h4> <div class="bar"><span></span></div> </div> <div class="opts"> <svg viewBox="0 0 24 24" class="del"> <path d="M3 13h18l-1.3 6.3A3 3 0 0 1 16.5 22h-9a3 3 0 0 1-3-2.7L3 13zM21 9H3V8h18v1zM10.7 3.7a1 1 0 0 0-1.4 0L7.3 5.7a1 1 0 1 0 1.4 1.4L10.7 5a1 1 0 0 0 0-1.4zM13.3 3.7a1 1 0 0 1 1.4 0l2 2a1 1 0 1 1-1.4 1.4L13.3 5a1 1 0 0 1 0-1.4z"/> </svg> </div> </div> <button>Tải lên</button> </div>
CSS
Copy
.up-img{ max-width:400px; margin:0 auto; text-align:center } .up-img .box { position: relative; border: 2px dashed #307dca; border-radius: 15px; padding-top: 50%; cursor: pointer; overflow: hidden; } .up-img .box.err { border-color: red; } .up-img .box.drag { background: rgba(0,0,0,0.05); } .up-img .box.preview { background: #3d4852; border: none; } .up-img .box.preview .init { display: none; } .up-img .box.progress { pointer-events: none; border: 2px solid #eee; } .up-img .inner, .up-img .prog { position: absolute; inset: 0; display: flex; justify-content: center; align-items: center; flex-direction: column; } .up-img .init img { width: 15%; } .up-img .init h3 { margin-top: 10px; font-weight: 500; } .up-img .prog { background: #fafafa; display: none; z-index: 10; } .up-img .box.progress .prog { display: flex; } .up-img .prog h4 { position: absolute; top: 40%; color: #555; } .up-img .bar { width: 90%; height: 5px; background: #eee; border-radius: 50px; overflow: hidden; } .up-img .bar span { display: block; height: 100%; background: #307dca; width: 0; } .up-img .opts { position: absolute; inset: 0; background: rgba(0,0,0,0.45); display: flex; justify-content: center; align-items: center; opacity: 0; transition: .3s; } .up-img .box.preview:hover .opts { opacity: 1; } .up-img button { margin-top: 10px; width: 100%; height: 40px; background: #307dca; color: #fff; border-radius: 8px; cursor: pointer; } .up-img button:hover { opacity: .6; }
JS
Copy
const UploadImg = (sel, opt = {}) => { const root = document.querySelector(sel); if (!root) return; const box = root.querySelector(".box"); const input = root.querySelector("input[type='file']"); const init = root.querySelector(".init"); const del = root.querySelector(".del"); const btn = root.querySelector("button"); const prog = root.querySelector(".prog"); const bar = prog.querySelector(".bar span"); const percent = prog.querySelector("h4 span"); const api = opt.api || "/"; let file = null; let url = null; const fmtSize = (s, d = 2) => { const k = 1024, i = Math.floor(Math.log(s) / Math.log(k)); return `${(s / Math.pow(k, i)).toFixed(d)} ${["B","KB","MB","GB"][i]}`; }; const setPreview = (f) => { const inner = root.querySelector(".inner"); const name = f.name || "Ảnh"; const size = fmtSize(f.size); if (url) URL.revokeObjectURL(url); url = URL.createObjectURL(f); const html = `<div class="prev"><div class="info"><h3>${name}</h3><span>${size}</span></div><img src="${url}"/></div>`; if (!box.classList.contains("preview")) { box.classList.add("preview"); inner.insertAdjacentHTML("beforeend", html); } else { const prev = inner.querySelector(".prev img"); const t = inner.querySelector(".prev h3"); const s = inner.querySelector(".prev span"); prev.src = url; t.textContent = name; s.textContent = size; } }; const reset = () => { const inner = root.querySelector(".inner"); file = null; input.value = ""; box.classList.remove("preview"); inner.querySelector(".prev")?.remove(); }; const change = (e) => { file = e.target.files[0]; if (file) setPreview(file); }; const drag = (e, s) => { e.preventDefault(); box.classList.toggle("drag", s); init.querySelector("h3").textContent = s ? "Thả ảnh vào" : "Kéo thả hoặc click để tải ảnh lên"; }; const upload = () => { if (!file) { box.classList.add("err"); setTimeout(() => box.classList.remove("err"), 1000); return; } const fd = new FormData(); fd.append(input.name || "image", file); const xhr = new XMLHttpRequest(); box.classList.add("progress"); xhr.upload.onprogress = (e) => { const p = Math.round((e.loaded / e.total) * 100); bar.style.width = `${p}%`; percent.textContent = `${p}%`; }; xhr.onload = () => { box.classList.remove("progress"); bar.style.width = 0; alert("Upload complete"); }; xhr.onerror = () => { box.classList.remove("progress"); alert("Upload error"); }; xhr.open("POST", api, true); xhr.send(fd); }; // Event bindings input.addEventListener("change", change); box.addEventListener("click", () => input.click()); box.addEventListener("dragover", (e) => drag(e, true)); box.addEventListener("dragleave", (e) => drag(e, false)); box.addEventListener("drop", (e) => { e.preventDefault(); drag(e, false); if (e.dataTransfer.files[0]) { const dt = new DataTransfer(); dt.items.add(e.dataTransfer.files[0]); input.files = dt.files; change({ target: input }); } }); del.addEventListener("click", (e) => { e.stopPropagation(); reset(); }); btn.addEventListener("click", upload); }; UploadImg("#upImg", { api: "https://www.filestackapi.com/api/file/store/S3?key=AUXfX4gYFQZOTUEgjqKXLz", });