vue image cropping component

vue image cropping component

Introduction

The image cropping component is the same as the uploading image component, and it is used more. The vue-cropper package is mainly used here, which supports functions such as cropping image file type verification, image size verification, image resolution verification, and image ratio verification. .

Mainly rely on instructions (install first, steps omitted)

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

text

1. Components

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. Use

<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. Supplement src/utils/upload.js file

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. Use effect

Insert picture description here

Guess you like

Origin blog.csdn.net/qq_39953537/article/details/99967286