公共組件:vue
<template>
<div>
<div class="upload-box">
<div class="image-box" v-if="imageUrl" :style="{'background-image': 'url('+ imageUrl +')', 'height': imageHeight}"></div>
<div class="upload">
<h6 class="upload-des">支持 jpg、png 格式大小 5M 之內的圖片</h6>
<input type="file" @change="getFile" ref="file" id="file">
<label for="file">點擊上傳</label>
</div>
</div>
<!-- vueCropper 剪裁圖片實現-->
<div class="vue-cropper-box" v-if="isShowCropper">
<div class="vue-cropper-content">
<vueCropper ref="cropper" :img="option.img" :outputSize="option.outputSize" :outputType="option.outputType" :info="option.info" :canScale="option.canScale" :autoCrop="option.autoCrop" :autoCropWidth="option.autoCropWidth" :autoCropHeight="option.autoCropHeight" :fixed="option.fixed" :fixedNumber="option.fixedNumber">
</vueCropper>
</div>
</div>
<el-button v-if="isShowCropper" type="danger" @click="onCubeImg">肯定裁剪圖片</el-button>
</div>
</template>
<script> import VueCropper from "vue-cropper" export default { name: 'Avatar', data () { return { //裁剪組件的基礎配置option
option: { img: '', //裁剪圖片的地址
info: true, //裁剪框的大小信息
outputSize: 1, // 裁剪生成圖片的質量
outputType: 'png', //裁剪生成圖片的格式
canScale: false, // 圖片是否容許滾輪縮放
autoCrop: true, // 是否默認生成截圖框
autoCropWidth: 195, // 默認生成截圖框寬度
autoCropHeight: 10, // 默認生成截圖框高度
fixed: false, //是否開啓截圖框寬高固定比例
fixedNumber: [1, 1] //截圖框的寬高比例
}, isShowCropper: false //是否顯示截圖框
} }, props: { // 特殊配置
cropperOption: { type: Object, default: () => { return null } }, // 默認圖片
imageUrl: { type: String, default: '' }, // 圖片展現高度
imageHeight: { type: String, default: '100px' } }, components: { VueCropper }, methods: { getFile (e) { let _this = this
var files = e.target.files[0] if (!e || !window.FileReader) return // 看支持不支持FileReader
let reader = new FileReader() reader.readAsDataURL(files) reader.onloadend = function () { _this.isShowCropper = true _this.option.img = this.result _this.$refs.file.value = '' } }, // 肯定裁剪圖片
onCubeImg () { this.$refs.cropper.getCropData((data) => { this.isShowCropper = false
//console.log("壓縮前的圖片大小:" + data.length)
let img = new Image(), _this = this img.src = data img.onload = function() { //let _data = _this.compress(img)
let blob = _this.dataURItoBlob(data) let formData = new FormData() formData.append("icon", blob) //console.log("將blob對象轉成formData對象:" + formData.get("icon"))
_this.$emit('cropped', data, formData) } }) }, // 壓縮圖片
compress(img) { let canvas = document.createElement("canvas"), ctx = canvas.getContext("2d"), initSize = img.src.length, width = img.width, height = img.height; canvas.width = width canvas.height = height // 鋪底色
ctx.fillStyle = "#fff" ctx.fillRect(0, 0, canvas.width, canvas.height) ctx.drawImage(img, 0, 0, width, height) //進行壓縮
let ndata = canvas.toDataURL("image/jpeg", 0.8) //console.log("壓縮後的圖片大小:" + ndata.length)
return ndata }, // base64轉成bolb對象
dataURItoBlob(base64Data) { let byteString if (base64Data.split(",")[0].indexOf("base64") >= 0) byteString = atob(base64Data.split(",")[1]) else byteString = unescape(base64Data.split(",")[1]) let mimeString = base64Data .split(",")[0] .split(":")[1] .split(";")[0]; let ia = new Uint8Array(byteString.length); for (let i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ia], { type: mimeString }) }, // 初始化配置文件
initOptions () { this.isShowCropper = false
if (this.cropperOption) { for (let key in this.option) { this.option[key] = this.cropperOption[key] || this.option[key] } } } }, created () { this.initOptions() } } </script>
<style scoped lang="stylus" rel="stylesheet"> .upload-box { & > div { display inline-block vertical-align middle } & .upload-des { margin 0 font-weight 400 color #909399 } } .image-box { width 100px height 100px margin-right 20px background-size 100px auto background-position left center background-repeat no-repeat } .upload { & label { width: 80px; height: 24px; font-size: 12px; line-height: 24px; display: inline-block; border-radius: 4px; text-align: center; border: 1px solid #ddd; cursor pointer } } input[type='file'] { display: none; } .vue-cropper-box { width: 60%; height: 200px; margin: 15px 0px; .vue-cropper-content { height 200px } } </style>
調用canvas
<el-form-item label="頭像" prop="icon" :rules="rules.required">
<Avatar v-on:cropped="doCrop" :cropperOption="cropperOption" :imageUrl="guestInfo.icon"></Avatar>
</el-form-item>
注意下面這個二進制文件接收和上傳的問題app
resetForm(){ this.$nextTick(() => { this.$refs.guest.clearValidate() this.guestUpInfo = new FormData() }) },
//完成裁剪
doCrop(icon, file){ this.guestInfo.icon = icon this.guestUpInfo = file }
//新增、編輯提交
submitGuest(){ validCallback(this, 'guest', () => { this.guestUpInfo.append('name',this.guestInfo.name) this.guestUpInfo.append('position',this.guestInfo.position) this.guestUpInfo.append('description',this.guestInfo.description) if(this.guestInfo.seq){ this.guestUpInfo.append('seq',this.guestInfo.seq) } if(this.editGuest){ this.guestUpInfo.append('id',this.guestInfo.id) } publicSubmitApi('saveSpeaker', this.guestUpInfo, true).then(res => { if (res.status === 200) { this.guestShow = false
this.fetchData() this.$message({ message: this.editGuest ? '編輯成功' : '新增成功', type: 'success' }) } }) }) },