天天看點

vue圖檔裁剪元件vue圖檔裁剪元件

vue圖檔裁剪元件

簡介

圖檔裁剪元件同上傳圖檔元件一樣,用的較多,這裡主要用的是vue-cropper這個包,功能支援裁剪圖檔檔案類型檢驗,圖檔大小檢驗,圖檔分辨率校驗以及圖檔比列校驗等功能。

主要依賴說明 (先安裝,步驟略)

{
    "element-ui": "2.11.1",  
    "vue": "^2.6.10",
    "vue-router": "^3.0.1",
    "vue-cropper": "^0.4.7"
 }
           

正文

1.元件

src/components/Cropper.vue

<template>
  <div class="custom-upload">
    <el-dialog
      title="圖檔裁剪"
      :visible.sync="showCropper"
      top="6vh"
      width="50%"
      height="600"
      class="cropper-dialog"
      center
      append-to-body
    >
      <vue-cropper
        v-if="showCropper"
        id="corpper"
        ref="cropper"
        :class="{'corpper-warp':showCropper}"
        v-bind="cropper"
      />
      <div v-if="showCropper" class="cropper-button">
        <el-button class="cancel-btn" size="small" @click.native="showCropper=false">取消</el-button>
        <el-button size="small" type="primary" :loading="loading" @click="uploadCover">完成</el-button>
      </div>
    </el-dialog>
    <input
      :id="id"
      type="file"
      style="display: none"
      name="single"
      accept="image/*"
      @change="onChange($event)"
    />

    <el-button size="small" type="primary" :loading="loading" @click="handleOpenFile()">
      <i class="fa fa-upload" />
      {{ buttonName }}
    </el-button>
    <div v-if="tips" class="tips clear-margin-top">{{ tips }}</div>
  </div>
</template>

<script>
// 上傳檔案元件
import { VueCropper } from 'vue-cropper'

// 定義的接口根據自己項目更換
import { uploadImage } from '@/api/upload'

import { isImageFile, isMaxFileSize, readFile } from '@/utils/upload'   // 見下文
import { Message } from 'element-ui'

export default {
  components: {
    VueCropper
  },
  props: {
    // 最大上傳檔案的大小
    maxFileSize: {
      type: Number,
      default: 2 // (MB)
    },
    // 按鈕文字
    buttonName: {
      type: String,
      default: '添加圖檔'
    },
    // 提示内容
    tips: {
      type: String
    },
    // 圖檔裁剪比列
    fixedNumber: {
      type: Array,
      default: function() {
        return []
      }
    },
    // 圖檔檔案分辨率的寬度
    width: {
      type: Number,
      default: 460
    },
    // 圖檔檔案分辨率的高度
    height: {
      type: Number,
      default: 300
    }
  },
  data() {
    return {
      id: 'cropper-input-' + +new Date(),
      loading: false,
      showCropper: false,
      cropper: {
        img: '',
        info: true,
        size: 0.9,
        outputType: 'png',
        canScale: true,
        autoCrop: true,
        full: true,
        // 隻有自動截圖開啟 寬度高度才生效
        autoCropWidth: this.width,
        autoCropHeight: this.height,
        fixedBox: false,
        // 開啟寬度和高度比例
        fixed: true,
        fixedNumber: this.fixedNumber,
        original: false,
        canMoveBox: true,
        canMove: true
      }
    }
  },
  methods: {
    // 打開檔案
    handleOpenFile() {
      const input = document.getElementById(this.id)
      // 解決同一個檔案不能監聽的問題
      input.addEventListener(
        'click',
        function() {
          this.value = ''
        },
        false
      )
      // 點選input
      input.click()
    },

    // 裁剪input 監聽
    async onChange(e) {
      const file = e.target.files[0]
      if (!file) {
        return Message.error('選擇圖檔失敗')
      }
      // 驗證檔案類型
      if (!isImageFile(file)) {
        return
      }
      try {
        // 讀取檔案
        const src = await readFile(file)
        this.showCropper = true
        this.cropper.img = src
      } catch (error) {
        console.log(error)
      }
    },

    // 封面上傳功能
    uploadCover() {
      this.$refs.cropper.getCropBlob(async imgRes => {
        try {
          // 檔案大小限制
          if (!isMaxFileSize(imgRes, this.maxFileSize)) {
            return
          }
          this.loading = true
          const url = await uploadImage(imgRes)
          this.$emit('subUploadSucceed', url)
          Message.success('上傳成功')
          this.loading = false
          this.showCropper = false
        } catch (error) {
          this.loading = false
          this.showCropper = false
          Message.error(error.data.message)
        }
      })
    }
  }
}
</script>

<style lang="scss"  >
#corpper {
  width: 90%;
  height: 400px;
  margin: 0 auto;
  background-image: none;
  background: #fff;
  z-index: 1002;
}
.cropper-dialog {
  height: 800px;
  text-align: center;
  .el-dialog__header {
    padding-top: 15px;
  }
  .el-dialog--center .el-dialog__body {
    padding-top: 0;
    padding-bottom: 15px;
  }
  .el-dialog {
    text-align: center;
  }
}
.cropper-button {
  z-index: 1003;
  text-align: center;
  margin-top: 20px;
  .el-button {
    font-size: 16px;
    cursor: pointer;
    text-align: center;
  }
  .cancel-btn {
    color: #373737;
  }
  .el-button:last-child {
    margin-left: 100px;
  }
}
.cropper-modal {
  background-color: rgba(0, 0, 0, 0.5) !important;
}
.custom-upload {
  .tips {
    margin-top: 10px;
    color: red;
    font-size: 12px;
  }
  .clear-margin-top {
    margin-top: 0;
  }
}
</style>


           

2.使用

<template>
    <div v-if="url">
         <img :src="url" height="160" />
    </div>
    <div>
        <App-cropper
            :width="300"
            :height="300"
            :fixed-number="[1,1]"
            @subUploadSucceed="getShopImages"
        />
    </div>
</template>

<script>
import AppCropper from '@/components/Cropper'
export default {
  name: 'GoodsForm',
  components: {
    AppCropper
  },
  data() {
    return {
      url: ''
    }
  },
  methods: {
    // 海報上傳成功
    handleUploadSucceed(url) {
      this.url = url
    }
  }
}
</script>
 
           

3.補充src/utils/upload.js 檔案

import { Message } from 'element-ui'

/**
 *
 * @param {file} file 源檔案
 * @desc 限制為圖檔檔案
 * @retutn 是圖檔檔案傳回true否則傳回false
 */
export const isImageFile = (file,fileTypes) => {
  const types =fileTypes|| [
    'image/png',
    'image/gif',
    'image/jpeg',
    'image/jpg',
    'image/bmp',
    'image/x-icon'
  ]
  const isImage = types.includes(file.type)
  if (!isImage) {
    Message.error('上傳檔案非圖檔格式!')
    return false
  }

  return true
}

/**
 *
 * @param {file} file 源檔案
 * @param {number} fileMaxSize  圖檔限制大小機關(MB)
 * @desc 限制為檔案上傳大小
 * @retutn 在限制内傳回true否則傳回false
 */
export const isMaxFileSize = (file, fileMaxSize = 2) => {
  const isMaxSize = file.size / 1024 / 1024 < fileMaxSize
  if (!isMaxSize) {
    Message.error('上傳頭像圖檔大小不能超過 ' + fileMaxSize + 'MB!')
    return false
  }
  return true
}

/**
 *
 * @param {file} file 源檔案
 * @desc 讀取圖檔檔案為base64檔案格式
 * @retutn 傳回base64檔案
 */
export const readFile = file => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.onload = e => {
      const data = e.target.result
      resolve(data)
    }
    reader.onerror = () => {
      const err = new Error('讀取圖檔失敗')
      reject(err.message)
    }

    reader.readAsDataURL(file)
  })
}

/**
 *
 * @param {string} src  圖檔位址
 * @desc 加載真實圖檔
 * @return 讀取成功傳回圖檔真實寬高對象 ag: {width:100,height:100}
 */
export const loadImage = src => {
  return new Promise((resolve, reject) => {
    const image = new Image()
    image.src = src
    image.onload = () => {
      const data = {
        width: image.width,
        height: image.height
      }
      resolve(data)
    }
    image.onerror = () => {
      const err = new Error('加載圖檔失敗')
      reject(err)
    }
  })
}

/**
 *
 * @param {file} file 源檔案
 * @param {object} props   檔案分辨率的寬和高   ag: props={width:100, height :100}
 * @desc  判斷圖檔檔案的分辨率是否在限定範圍之内
 * @throw  分辨率不在限定範圍之内則抛出異常
 *
 */
export const isAppropriateResolution = async(file, props) => {
  try {
    const { width, height } = props
    const base64 = await readFile(file)
    const image = await loadImage(base64)
    if (image.width !== width || image.height !== height) {
      throw new Error('上傳圖檔的分辨率必須為' + width + '*' + height)
    }
  } catch (error) {
    throw error
  }
}

/**
 *
 * @param {file} file 源檔案
 * @param {array} ratio   限制的檔案比例 ag:  ratio= [1,1]
 * @desc 判斷圖檔檔案的比列是否在限定範圍
 * @throw  比例不在限定範圍之内則抛出異常
 */
export const isAppRatio = async(file, ratio) => {
  try {
    const [w, h] = ratio
    if (h === 0 || w === 0) {
      const err = '上傳圖檔的比例不能出現0'
      Message.error(err)
      throw new Error(err)
    }
    const base64 = await readFile(file)
    const image = await loadImage(base64)
    if (image.width / image.height !== w / h) {
      throw new Error('上傳圖檔的寬高比例必須為 ' + w + ' : ' + h)
    }
  } catch (error) {
    throw error
  }
}

           

4.使用效果

vue圖檔裁剪元件vue圖檔裁剪元件