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