Vue2組件、功能插件實例運用 - vue-cropper(圖片裁剪)


vue-cropper

簡介:一個優雅的圖片裁剪插件。就一句話,沒得更多的廢話

官網:https://github.com/xyxiao001/vue-cropper

1、組件的安裝

  npm安裝

npm install vue-cropper --save-dev

  yarn安裝

yarn add vue-cropper    --save-dev

2、使用

  2.1 主頁面圖片選擇(element-ui上傳組件)

<!--圖片選擇(element-ui上傳組件)-->
<el-upload
  class="upload-demo"
  ref="upload"
  action=""
  drag
  :auto-upload="false"
  :multiple="false"
  :limit="1"
  :show-file-list="false"
  :on-change="changeFile"
>
  <i class="el-icon-upload"></i>
  <div class="el-upload__text">點擊上傳</div>
  <div class="el-upload__tip">支持絕大多數圖片格式,單張圖片最大支持5MB</div>
</el-upload>

// changeFile方法
/**
 * 選擇圖片及限制圖片大小
 */
changeFile(file, fileList) {
  const isLt5M = file.size / 1024 / 1024 < 5
  if (!isLt5M) {
    this.$message.error('上傳文件大小不能超過 5MB!')
    return false
  }
  this.fileInfo = file
  // 圖片選擇好后,將圖片數據轉換成Blob本地地址並賦值對應參數后,最后顯示圖片裁剪框
  this.$nextTick(() => {
    this.dialogTitle = '基於vue的圖片裁剪'
    this.dialogWidth = '500'
    this.dialogPar = {
      imgUrl: URL.createObjectURL(file.raw), // 裁剪組件無法直接訪問圖片文件信息,故將文件信息轉成本地Blob數據地址,讓裁剪組件可以直接訪問
      fileInfo: file
    }
    this.dialogTemplate = 'imageCropper' // 本地封裝的圖片裁剪彈出窗組件
  })
},

  2.2 圖片裁剪彈出窗組件

  組件的具體使用參照官方文檔 https://github.com/xyxiao001/vue-cropper,vue2項目內全局引用 和 組件內引用 用法不一樣

  

  本次Demo采用組件內引入,組件的具體封裝,各部分代碼如下:

  2.2.1 template模板:

<!--VueCropper圖片裁剪組件加載,及該組件部分常用方法調用-->
<div class="el-col el-col-24">
  <div class="cropper-w">
    <div class="cropper text-align-c">
      <VueCropper
        ref="cropper"
        :img="option.img"
        :outputSize="option.size"
        :outputType="option.outputType"
        :info="true"
        :full="option.full"
        :canMove="option.canMove"
        :canMoveBox="option.canMoveBox"
        :original="option.original"
        :autoCrop="option.autoCrop"
        :fixed="option.fixed"
        :fixedNumber="option.fixedNumber"
        :centerBox="option.centerBox"
        :infoTrue="option.infoTrue"
        :fixedBox="option.fixedBox"
      ></VueCropper>
    </div>
  </div>
  <div class="el-col el-col-24 flex-layout btn-w">
    <el-button type="success" @click.stop="rotateLeft">向左旋轉</el-button>
    <el-button type="info" @click.stop="rotateRight">向右旋轉</el-button>
    <el-button type="primary" @click.stop="getCropData">獲取base64數據</el-button>
    <el-button type="primary" @click.stop="getCropBlob">獲取blob數據</el-button>
  </div>
</div>

  2.2.2 配套樣式:

<style scoped lang="less">
/*配套樣式*/
.cropper-w {
  .cropper {
    width: auto;
    height: 50vh;
  }
}
.btn-w {
  margin-top: 20px;
}
</style>

  2.2.3 組件引用及功能函數:

<script>
// 具體使用參照官方文檔 https://github.com/xyxiao001/vue-cropper
// vue2 全局引用 和 組件內引用 用法不一樣
import { VueCropper } from 'vue-cropper'

export default {
  name: 'imageCropper',
  components: { VueCropper },
  props: {
    dialogPar: {
      type: Object,
      required: false,
      default: () => {
        return {}
      }
    }
  },
  data() {
    return {
      // 裁剪組件基礎配置option,更多屬性或更多具體說明參考官方文檔
      option: {
        img: this.dialogPar.imgUrl, // 裁剪圖片的地址
        info: true, // 裁剪框的大小信息
        outputSize: 0.8, // 裁剪生成圖片的質量
        outputType: 'jpeg', // 裁剪生成圖片的格式(jpg(jpg 需要傳入jpeg))
        canScale: false, // 圖片是否允許滾輪縮放(這個屬性貌似沒得用,不管設置true,false都可以滾輪縮放)
        autoCrop: true, // 是否默認生成截圖框
        // autoCropWidth: 300, // 默認生成截圖框寬度
        // autoCropHeight: 200, // 默認生成截圖框高度
        fixedBox: true, // 固定截圖框大小 不允許改變
        fixed: true, // 是否開啟截圖框寬高固定比例
        fixedNumber: [1, 1], // 截圖框的寬高比例(這是比例,按兩個值的比值大小進行截圖框的寬高的設置,[1,1]和[100,100]是一樣的)
        full: true, // 是否輸出原圖比例的截圖
        canMove: true, // 上傳圖片是否可以移動
        canMoveBox: false, // 截圖框能否拖動
        original: false, // 上傳圖片按照原始比例渲染
        centerBox: false, // 截圖框是否被限制在圖片里面
        infoTrue: true // true 為展示真實輸出圖片寬高 false 展示看到的截圖框寬高
      },
      base64Data: {
        dataURL: '', // 用url方式表示的base64圖片數據
        type: 'image/jpeg' //文件類型
      }
    }
  },
  methods: {
    /**
     * 向左邊旋轉90度
     */
    rotateLeft() {
      this.$refs.cropper.rotateLeft() // 只能固定向左邊旋轉90度,不接受設定旋轉角度
    },
    /**
     * 向右邊旋轉90度
     */
    rotateRight() {
      this.$refs.cropper.rotateRight() // 只能固定向右邊旋轉90度,不接受設定旋轉角度
    },

    /**
     * 將 base64數據 直接轉換為 file對象
     */
    dataUrlToFile: function (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 })
    },

    /**
     * base64 轉Blob
     */
    base64ToBlob(base64Data) {
      let arr = base64Data.dataURL.split(',')
      let mime = arr[0].match(/:(.*?);/)[1] || base64Data.type
      // 去掉url的頭,並轉化為byte
      let bytes = window.atob(arr[1])
      // 處理異常,將ascii碼小於0的轉換為大於0
      let ab = new ArrayBuffer(bytes.length)
      // 生成視圖(直接針對內存):8位無符號整數,長度1個字節
      let ia = new Uint8Array(ab)
      for (let i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i)
      }
      return new Blob([ab], {
        type: mime
      })
    },

    /**
     * 獲取截圖的 base64 數據
     */
    getCropData() {
      this.$refs.cropper.getCropData(data => {
        // do something
        // 由於服務端文件上傳接口統一接收file對象,從而 base64編碼 數據在提交前需要將編碼數據轉為file對象
        // 將 base64編碼數據 轉成 file對象,有兩種方式
        // 1、直接用 new File()方法 把 base64數據 轉成 file對象
        let file = this.dataUrlToFile(data, this.dialogPar.fileInfo.name)

        // 2、先將 base64編碼數據 轉成 blob數據,然后用file對象內置方法,將blob數據轉成file對象
        // this.base64Data.dataURL = data
        // let blobData = this.base64ToBlob(this.base64Data)
        // const file = new window.File([blobData], this.dialogPar.fileInfo.name, { type: data.type })

        // 獲取圖片數據后,將數據上傳給父組件(imgUrl:圖片預覽鏈接地址,fileData:截取的圖片數據)
        // 由於 base64 鏈接數據,img標簽src屬性可以直接使用,故此處不做處理
        this.$emit('emitPar', { imgUrl: data, fileData: file })
      })
    },

    /**
     * 獲取截圖的 blob 數據
     */
    getCropBlob() {
      this.$refs.cropper.getCropBlob(data => {
        // do something
        // 由於服務端文件上傳接口統一接收file對象,從而數據在提交前需要將blob數據轉為file對象,轉換方法如下:
        const file = new window.File([data], this.dialogPar.fileInfo.name, { type: data.type })
        // 獲取圖片數據后,將數據上傳給父組件(imgUrl:圖片本地預覽 blob數據地址,fileData:截取的圖片數據 轉換成的file對象)
        this.$emit('emitPar', { imgUrl: URL.createObjectURL(data), fileData: file })
      })
    }
  }
}
</script>

  2.3 裁剪好的圖片 文件上傳

  在 2.2介紹的組件中 我們已經裁剪出我們需要的圖片,並通過 $emit方法 將對應的數據上傳到了父組件(主頁面)中,此時就需要調用上傳接口將圖片信息上傳到對應的服務器了,從而更新對應的所需圖片數據。

/**
 * 根據子組件上傳值,控制Dialog 對話框顯隱
 * @param {Object} data:子組件(圖片裁剪)上傳到父組件數據<br/>
 * 例: {state: 控制狀態(默認關閉:false)}
 */
receiveEmitPar(data) {
  this.dialogTemplate = ''
  if (data && data.imgUrl) {
    this.previewImgUrl = data.imgUrl  // 本地預覽地址
    this.fileInfo.raw = data.fileData // 由於文件上傳(包括文件和圖片)http request統一封裝為讀取raw屬性,故此處將子組件轉換好的file對象賦值給raw屬性
    this.$http.vueCropper.uploadPublicFile(this.fileInfo).then(res => {
      this.serverAddressImgUrl = res.realUrl  // 上傳后返回的服務器地址 圖片預覽
    })
  }
  this.$refs.upload.uploadFiles = []
}

  上傳返回的服務器圖片地址

  本地預覽和上傳后服務器圖片地址預覽

3、將 Blob數據類型轉成File類型,base64編碼數據 轉成File類型

上文中因為服務端文件上傳接口統一接收file對象,所以裁剪后的圖片數據不管是 blob數據類型 還是 base64編碼數據,都要轉成file對象才能上傳。而這里面不管是將 blob數據類型直接轉成file對象,還是將 base64編碼數據先轉成 blob數據類型再轉成file對象,都繞不開 File()構造函數

File()構造器 創建新的 File 對象實例,語法:

var myFile = new File(bits, name[, options]);

4、總結

總的來說 vue-cropper 是一款很優秀的圖片裁剪組件,可以讓我們在日常開發中涉及到一些圖片裁剪需求時減少很大工作量;但美中不足的是,該款組件只能裁剪出正方形或長方形的圖片【雖然后面可以用樣式 border-radius: 50% 讓圖片顯示成圓形】,但有時候有些奇葩產品就是要直接裁剪成圓形,這個時候就比較難搞了,又不得不重新造輪子。

最后,非常感謝這些封裝插件的大佬,正是這些輪子的問世,使得前端開發減少了不知多少工作量,也讓這些功能看起來更高大上更美觀了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM