美化上傳按鈕
在ejs 頁面
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></meta><title><%= title %></title><link rel='stylesheet' href='/stylesheets/form.css'/><!-- 本地 Bootstrap 引入方式 --><link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet"><script src="/bootstrap/js/bootstrap.bundle.min.js"></script><script src="./javascripts/image-upload.js"></script>
</head>
<body>
<div class="container"><h1>文件上傳</h1><from action="/upload-img" method="post" enctype="multipart/form-data"><label class="upload-container"><span class="upload-icon" id="uploadIcon">+</span><input accept="image/*" id="avatarInput" type="file" /><img id="avatarPreview" class="preview-img d-done" /><button type="button" class="remove-btn" id="removeBtn">x</button></label><button type="submit" class="btn btn-primary mt-3">提交</button></from>
</div>
</body>
</html>
樣式
.upload-container{width: 150px;height: 150px;border: 2px solid #ccc;border-radius: 10px;display: flex;align-items: center;justify-content: center;cursor: pointer;position: relative;overflow: hidden;background-color: #f8f9fa;transition:border-color 0.3s;.upload-icon{font-size:2rem;color:#999;//禁用鼠標事件,該元素變得“不可點擊”、“鼠標穿透”pointer-events: none; //用來控制一個元素是否能響應鼠標事件(點擊、懸停、拖動等)}.preview-img{width:100%;height: 100%;object-fit: cover;position: absolute;top: 0;left: 0;}.remove-btn{position: absolute;top:5px;right:5px;background-color:rgba(0,0,0,.6);color:#ffffff;border:none;border-radius:50%;width:24px;height:24px;display:none;align-items: center;justify-content: center;cursor:pointer;font-weight: bold;}
}.upload-container:hover{border-color: #007bff;
}.upload-container input[type="file"]{position: absolute;opacity: 0;height: 100%;width: 100%;cursor: pointer;
}
完成效果
這個效果沒有裁剪,進度條、多圖片上傳
上傳進度條
<div class="progress mt-2 d-done" id="progressWrapper"><div class="progress-bar" role="progressbar" id="uploadProgress" style="width: 0%">0%</div></div>
document.getElementById('uploadForm').addEventListener('submit', function(e){e.preventDefault();// const blob =preview._blob;// if(!blob){// alert("請先選擇并裁剪頭像");// return;// }const file =input.files[0];if(!file) return alert('Please upload a valid image!');let formData = new FormData();formData.append('file', file);let xhr = new XMLHttpRequest();xhr.open('POST', '/upload-avatar');xhr.upload.addEventListener('progress', (e) => {if(e.lengthComputable){let percent = Math.round((e.loaded / e.total) * 100);progressWrapper.classList.remove('d-none');progressBar.style.width= percent + '%';progressBar.innerText = percent + '%';}})xhr.onload =function () {if(xhr.status === 200) {alert("successfully uploaded!");}else{alert("fail to upload");}}xhr.send(formData);})
使用cropperjs 裁剪圖片
使用npm安裝
npm install cropperjs@1.5.13
? 關鍵點:cropperjs@2.x 為模塊化版本,不再提供 dist/ 下的瀏覽器用 JS/CSS 文件
從 v2.0.0 開始,cropperjs 改為 純 ESM(ES Module)包,它不再包含:
- cropper.js
- cropper.css
- dist/ 文件夾
📌 小提示
版本 | 適合人群 | 是否自帶 dist/ |
---|---|---|
1.5.13 | 普通瀏覽器/EJS 項目 | ? 有 JS/CSS,推薦 |
2.x | Vite/Webpack 打包項目 | ? 無,需構建 |
引入js、css
<!--本地開發引入 cropper --><link rel="stylesheet" href="/cropper/cropper.css"><script src="/cropper/cropper.js"></script>
彈框
<!-- 裁剪模態框--><div class="modal fade modal-mask" id="cropModal" tabindex="1" aria-hidden="true"><div class="modal-dialog modal-dialog-centered modal-fullscreen"><div class="modal-content p-3"><div class="modal-body" style="width: 100%; height: 600px;"><img id="cropImg" /></div><div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button><button type="button" class="btn btn-success" id="cropConfirm">確認裁剪</button></div></div></div></div>
js
input.addEventListener('change', (e)=> {let that=e.target;let file=that.files[0];if(file && file.type.startsWith('image/')) {let reader=new FileReader();reader.onloadend = (e) => {//未裁剪前使用的代碼// preview.src = e.target.result;// preview.classList.remove('d-none');// removeBtn.style.display = 'flex';// uploadIcon.style.display = 'none';//裁剪時的代碼cropImage.src = e.target.result;new bootstrap.Modal(document.getElementById('cropModal')).show();cropImage.onload =()=>{if(cropper) cropper.destroy();cropper =new Cropper(cropImage,{aspectRatio: 1,autoCropArea:1,viewMode:2})}}reader.readAsDataURL(file);}})