溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

vue-upload-component如何封裝一個(gè)圖片上傳組件

發(fā)布時(shí)間:2021-07-24 13:45:01 來源:億速云 閱讀:644 作者:小新 欄目:web開發(fā)

這篇文章主要介紹了vue-upload-component如何封裝一個(gè)圖片上傳組件,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

需求分析

業(yè)務(wù)要求,需要一個(gè)圖片上傳控件,需滿足

  • 多圖上傳

  • 點(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ù)需求需要放大縮小,這個(gè)組件就不滿足了。

壓縮
可以通過 canvas 相關(guān)api來實(shí)現(xiàn)壓縮功能,還可以用一些第三方庫來實(shí)現(xiàn), 例如image-compressor.js

數(shù)據(jù)

因?yàn)楸韱雾撁嫔婕熬庉嫷那闆r,上傳組件為了展示優(yōu)雅點(diǎn),需要做點(diǎn)處理。首先就先要對(duì)數(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)去,略微麻煩。
  所以分兩次遍歷,一次遍歷表單對(duì)象里的圖片(直接用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"> <!-- 加幾個(gè)樣式來控制 `上傳中` 和 `上傳失敗` 的樣式-->
  <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對(duì)象
  }
 },
 components: {
  'file-upload': uploader
 },
 methods: {
  // 當(dāng) add, update, remove File 這些事件的時(shí)候會(huì)觸發(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)前文件對(duì)象
   this.$emit('update:value', this.value) // 更新值
   }
  }

  // 自動(dòng)上傳
  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)) {
   // 自動(dòng)壓縮
   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})
}

最終效果

vue-upload-component如何封裝一個(gè)圖片上傳組件

vue-upload-component如何封裝一個(gè)圖片上傳組件

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“vue-upload-component如何封裝一個(gè)圖片上傳組件”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI