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.使用效果

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaofei0627

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值