基于vue-upload-component封裝一個圖片上傳組件的示例
需求分析
業(yè)務(wù)要求,需要一個圖片上傳控件,需滿足
- 多圖上傳
- 點(diǎn)擊預(yù)覽
- 圖片前端壓縮
- 支持初始化數(shù)據(jù)
相關(guān)功能及資源分析
基本功能
先到https://www.npmjs.com/search?q=vue+upload上搜索有關(guān)上傳的控件,沒有完全滿足需求的組件,過濾后找到 vue-upload-component 組件,功能基本都有,自定義也比較靈活,就以以此進(jìn)行二次開發(fā)。
預(yù)覽
因?yàn)轫?xiàng)目是基于 vant 做的,本身就提供了 ImagePreview 的預(yù)覽組件,使用起來也簡單,如果業(yè)務(wù)需求需要放大縮小,這個組件就不滿足了。
壓縮
可以通過 canvas 相關(guān)api來實(shí)現(xiàn)壓縮功能,還可以用一些第三方庫來實(shí)現(xiàn), 例如image-compressor.js
數(shù)據(jù)
因?yàn)楸韱雾撁嫔婕熬庉嫷那闆r,上傳組件為了展示優(yōu)雅點(diǎn),需要做點(diǎn)處理。首先就先要對數(shù)據(jù)格式和服務(wù)端進(jìn)行約定,然后在處理剩下的
開發(fā)
需求和實(shí)現(xiàn)思路基本確定,開始進(jìn)入編碼,先搭建可運(yùn)行可測試的環(huán)境
第一步,創(chuàng)建相關(guān)目錄
|- 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">
<!--
這里分為兩段遍歷,理由是:在編輯情況下要默認(rèn)為組件添加默認(rèn)數(shù)據(jù),雖然說組件提供了 `add` 方法,
但在編輯狀態(tài)下,需要把 url 形式的圖片轉(zhuǎn)換成 File 之后才可以添加進(jìn)去,略微麻煩。
所以分兩次遍歷,一次遍歷表單對象里的圖片(直接用img標(biāo)簽展示,新上傳的圖片可以通過 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: {
// 當(dāng) add, update, remove File 這些事件的時候會觸發(fā)
inputFile(newFile, oldFile) {
// 上傳完成
if (newFile && oldFile && !newFile.active && oldFile.active) {
// 獲得相應(yīng)數(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) // 刪除當(dāng)前文件對象
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 字段 用于圖片預(yù)覽
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>
圖片壓縮也可以自己來實(shí)現(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)
// 進(jìn)行壓縮
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)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Vue 2.5 Level E 發(fā)布了: 新功能特性一覽
很高興Vue 2.5 Level E 發(fā)布了。在這篇文章中,我們將重點(diǎn)介紹一些更重要的的變化:更好的 TypeScript 集成,更好的錯誤處理,更好地支持單文件組件中的函數(shù)式組件以及與環(huán)境無關(guān)的服務(wù)端渲染2017-10-10
Vue項(xiàng)目的表單校驗(yàn)實(shí)戰(zhàn)指南
這篇文章主要介紹了Vue項(xiàng)目表單校驗(yàn)的相關(guān)資料,前端表單校驗(yàn)?zāi)軠p少無效請求,保護(hù)后端接口,使用ElementPlus表單組件進(jìn)行校驗(yàn),需要準(zhǔn)備表單對象、規(guī)則對象并進(jìn)行雙向綁定,用戶名、密碼以及協(xié)議勾選等字段都需符合特定規(guī)則,需要的朋友可以參考下2024-10-10
Vue2安裝使用MonacoEditor方式(自定義語法,自定義高亮,自定義提示)
這篇文章主要介紹了Vue2安裝使用MonacoEditor方式(自定義語法,自定義高亮,自定義提示),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
Ant?Design?Vue中的table與pagination的聯(lián)合使用方式
這篇文章主要介紹了Ant?Design?Vue中的table與pagination的聯(lián)合使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10

