图片裁剪组件同上传图片组件一样,用的较多,这里主要用的是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.使用效果