基于vue-upload-component封裝一個圖片上傳組件的示例
需求分析
業(yè)務要求,需要一個圖片上傳控件,需滿足
- 多圖上傳
- 點擊預覽
- 圖片前端壓縮
- 支持初始化數(shù)據(jù)
相關功能及資源分析
基本功能
先到https://www.npmjs.com/search?q=vue+upload
上搜索有關上傳的控件,沒有完全滿足需求的組件,過濾后找到 vue-upload-component
組件,功能基本都有,自定義也比較靈活,就以以此進行二次開發(fā)。
預覽
因為項目是基于 vant
做的,本身就提供了 ImagePreview
的預覽組件,使用起來也簡單,如果業(yè)務需求需要放大縮小,這個組件就不滿足了。
壓縮
可以通過 canvas
相關api來實現(xiàn)壓縮功能,還可以用一些第三方庫來實現(xiàn), 例如image-compressor.js
數(shù)據(jù)
因為表單頁面涉及編輯的情況,上傳組件為了展示優(yōu)雅點,需要做點處理。首先就先要對數(shù)據(jù)格式和服務端進行約定,然后在處理剩下的
開發(fā)
需求和實現(xiàn)思路基本確定,開始進入編碼,先搭建可運行可測試的環(huán)境
第一步,創(chuàng)建相關目錄
|- components |- ImageUpload |- ImageUpload.vue |- index.js
第二步,安裝依賴
$ npm i image-compressor.js -S $ npm i vue-upload-component -S
第三步,編寫核心主體代碼
// index.js import ImageUpload from './ImageUpload' export default ImageUpload
// ImageUpload.vue <template> <div class="m-image-upload"> <!-- 這里分為兩段遍歷,理由是:在編輯情況下要默認為組件添加默認數(shù)據(jù),雖然說組件提供了 `add` 方法, 但在編輯狀態(tài)下,需要把 url 形式的圖片轉(zhuǎn)換成 File 之后才可以添加進去,略微麻煩。 所以分兩次遍歷,一次遍歷表單對象里的圖片(直接用img標簽展示,新上傳的圖片可以通過 blob 來賦值 src),第二次遍歷組件里的 files --> <div class="file-item" v-for="(file, index) in value"> <img :src="file.thumb || file.url" @click="preview(index)" /> <van-icon name="clear" class="close" @click="remove(index, true)"/> <!-- 把圖片從數(shù)組中刪除 --> </div> <div :class="{'file-item': true, 'active': file.active, 'error': !!file.error}" v-for="(file, index) in files"> <!-- 加幾個樣式來控制 `上傳中` 和 `上傳失敗` 的樣式--> <img v-if="file.blob" :src="file.blob" /> <div class="uploading-shade"> <p>{{ file.progress }} %</p> <p>正在上傳</p> </div> <div class="error-shade"> <p>上傳失??!</p> </div> <van-icon name="clear" class="close" @click="remove(index)" /> </div> <file-upload ref="uploader" v-model="files" multiple :thread="10" extensions="jpg,gif,png,webp" post-action="http://localhost:3000/file/upload" @input-file="inputFile" @input-filter="inputFilter" > <van-icon name="photo"/> </file-upload> </div> </template> <script> /** * 圖片上傳控件 * 使用方法: import ImageUpload from '@/components/ImageUpload' ... components: { ImageUpload }, ... <image-upload :value.sync="pics"/> */ import uploader from 'vue-upload-component' import ImageCompressor from 'image-compressor.js'; import { ImagePreview } from 'vant'; export default { name: 'ImageUpload', props: { value: Array // 通過`.sync`來添加更新值的語法題,通過 this.$emit('update:value', this.value) 來更新 }, data() { return { files: [] // 存放在組件的file對象 } }, components: { 'file-upload': uploader }, methods: { // 當 add, update, remove File 這些事件的時候會觸發(fā) inputFile(newFile, oldFile) { // 上傳完成 if (newFile && oldFile && !newFile.active && oldFile.active) { // 獲得相應數(shù)據(jù) if (newFile.xhr && newFile.xhr.status === 200) { newFile.response.data.thumb = newFile.thumb // 把縮略圖轉(zhuǎn)移 this.value.push(newFile.response.data) // 把 uploader 里的文件賦值給 value this.$refs.uploader.remove(newFile) // 刪除當前文件對象 this.$emit('update:value', this.value) // 更新值 } } // 自動上傳 if (Boolean(newFile) !== Boolean(oldFile) || oldFile.error !== newFile.error) { if (!this.$refs.uploader.active) { this.$refs.uploader.active = true } } }, // 文件過濾,可以通過 prevent 來阻止上傳 inputFilter(newFile, oldFile, prevent) { if (newFile && (!oldFile || newFile.file !== oldFile.file)) { // 自動壓縮 if (newFile.file && newFile.type.substr(0, 6) === 'image/') { // && this.autoCompress > 0 && this.autoCompress < newFile.size(小于一定尺寸就不壓縮) newFile.error = 'compressing' // 壓縮圖片 const imageCompressor = new ImageCompressor(null, { quality: .5, convertSize: Infinity, maxWidth: 1000, }) imageCompressor.compress(newFile.file).then((file) => { // 創(chuàng)建 blob 字段 用于圖片預覽 newFile.blob = '' let URL = window.URL || window.webkitURL if (URL && URL.createObjectURL) { newFile.blob = URL.createObjectURL(file) } // 縮略圖 newFile.thumb = '' if (newFile.blob && newFile.type.substr(0, 6) === 'image/') { newFile.thumb = newFile.blob } // 更新 file this.$refs.uploader.update(newFile, {error: '', file, size: file.size, type: file.type}) }).catch((err) => { this.$refs.uploader.update(newFile, {error: err.message || 'compress'}) }) } } }, remove(index, isValue) { if (isValue) { this.value.splice(index, 1) this.$emit('update:value', this.value) } else { this.$refs.uploader.remove(this.files[index]) } }, preview(index) { ImagePreview({ images: this.value.map(item => (item.thumb || item.url)), startPosition: index }); } } } </script>
圖片壓縮也可以自己來實現(xiàn),主要是理清各種文件格式的轉(zhuǎn)換
compress(imgFile) { let _this = this return new Promise((resolve, reject) => { let reader = new FileReader() reader.onload = e => { let img = new Image() img.src = e.target.result img.onload = () => { let canvas = document.createElement('canvas') let ctx = canvas.getContext('2d') canvas.width = img.width canvas.height = img.height // 鋪底色 ctx.fillStyle = '#fff' ctx.fillRect(0, 0, canvas.width, canvas.height) ctx.drawImage(img, 0, 0, img.width, img.height) // 進行壓縮 let ndata = canvas.toDataURL('image/jpeg', 0.3) resolve(_this.dataURLtoFile(ndata, imgFile.name)) } } reader.onerror = e => reject(e) reader.readAsDataURL(imgFile) }) } // base64 轉(zhuǎn) Blob dataURLtoBlob(dataurl) { let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) } return new Blob([u8arr], {type: mime}) }, // base64 轉(zhuǎn) File dataURLtoFile(dataurl, filename) { let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) } return new File([u8arr], filename, {type: mime}) }
最終效果
參考資料
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
在Vue3中使用CSS Modules實現(xiàn)樣式隔離
隨著構建現(xiàn)代前端應用的需要,樣式的管理和隔離變得越來越重要,為了解決樣式?jīng)_突和管理困難的問題,CSS Modules 應運而生,本文我們將討論如何在 Vue3 中使用 CSS Modules 實現(xiàn)樣式隔離,需要的朋友可以參考下2024-09-09vue-cli2.0轉(zhuǎn)3.0之項目搭建的詳細步驟
這篇文章主要介紹了vue-cli2.0轉(zhuǎn)3.0之項目搭建的詳細步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12Vue中ref、reactive、toRef、toRefs、$refs的基本用法總結
這篇文章主要給大家介紹了關于Vue中ref、reactive、toRef、toRefs、$refs的基本用法,以及他們之家的區(qū)別,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-11-11Vue 實現(xiàn)高級穿梭框 Transfer 封裝過程
本文介紹了基于Vue2和Element-UI實現(xiàn)的高級穿梭框組件Transfer的設計與技術方案,組件支持多項選擇,并能實時同步已選擇項,包含豎版和橫版設計稿,并提供了組件的使用方法和源碼,此組件具備本地分頁和搜索功能,適用于需要在兩個列表間進行數(shù)據(jù)選擇和同步的場景2024-09-09淺談vue的props,data,computed變化對組件更新的影響
本篇文章主要介紹了淺談vue的props,data,computed變化對組件更新的影響,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01