1.安裝引入依賴包vue
npm i vue-cropper
在組件vueCropper引入依賴包web
import {VueCropper} from 'vue-cropper'
2.添加vueCropper.vue組件npm
<template> <div> <div class="cropper-content"> <!-- 剪裁框 --> <div class="cropper"> <vueCropper ref="cropper" :img="option.img" :outputSize="option.size" :outputType="option.outputType" :info="true" :full="option.full" :canMove="option.canMove" :canMoveBox="option.canMoveBox" :original="option.original" :autoCrop="option.autoCrop" :autoCropWidth="option.autoCropWidth" :autoCropHeight="option.autoCropHeight" :fixedBox="option.fixedBox" @realTime="realTime" :fixed="option.fixed" :fixedNumber="fixedNumber"></vueCropper> </div> <!-- 預覽框 --> <div class="show-preview" :style="{'width': '300px', 'height': '300px', 'overflow': 'hidden', 'margin': '0 25px', 'display':'flex', 'align-items' : 'center'}"> <div :style="previews.div" class="preview"> <img :src="previews.url" :style="previews.img"> </div> </div> </div> <div class="footer-btn"> <!-- 縮放旋轉按鈕 --> <div class="scope-btn"> <el-button type="primary" icon="el-icon-zoom-in" @click="changeScale(1)"></el-button> <el-button type="primary" icon="el-icon-zoom-out" @click="changeScale(-1)"></el-button> <el-button type="primary" @click="rotateLeft">逆時針旋轉</el-button> <el-button type="primary" @click="rotateRight">順時針旋轉</el-button> </div> <!-- 確認上傳按鈕 --> <div class="upload-btn"> <el-button type="primary" @click="uploadImg('blob')">上傳</el-button> </div> </div> </div> </template> <script> import {VueCropper} from 'vue-cropper' export default { data() { return { previews: {}, // 預覽數據 option: { img: '', // 裁剪圖片的地址 (默認:空) size: 1, // 裁剪生成圖片的質量 (默認:1) full: false, // 是否輸出原圖比例的截圖 選true生成的圖片會很是大 (默認:false) outputType: 'jpg', // 裁剪生成圖片的格式 (默認:jpg) canMove: true, // 上傳圖片是否能夠移動 (默認:true) original: true, // 上傳圖片按照原始比例渲染 (默認:false) canMoveBox: false, // 截圖框可否拖動 (默認:true) autoCrop: true, // 是否默認生成截圖框 (默認:false) autoCropWidth: this.imgW*(256/320), // 默認生成截圖框寬度 (默認:80%) autoCropHeight: this.imgH*(256/320), // 默認生成截圖框高度 (默認:80%) fixedBox: true, // 固定截圖框大小 不容許改變 (默認:false) fixed: false, // 是否開啓截圖框寬高固定比例 (默認:true) fixedNumber: [1, 1] // 截圖框比例 (默認:[1:1]) }, downImg: '#' } }, props: ['imgFile', 'fixedNumber','imgW','imgH'], methods: { changeScale(num) { // 圖片縮放 num = num || 1 this.$refs.cropper.changeScale(num) }, rotateLeft() { // 向左旋轉 this.$refs.cropper.rotateLeft() }, rotateRight() { // 向右旋轉 this.$refs.cropper.rotateRight() }, Update() { // this.file = this.imgFile this.option.img = this.imgFile.url }, realTime(data) { // 實時預覽 this.previews = data }, uploadImg(type) { // 將剪裁好的圖片回傳給父組件 event.preventDefault() let that = this if (type === 'blob') { this.$refs.cropper.getCropBlob(data => { const reader = new FileReader(); reader.readAsDataURL(data); reader.onload = (e) => { that.$emit('upload', reader.result) } }) } else { this.$refs.cropper.getCropData(data => { that.$emit('upload', data) }) } } }, components: {VueCropper} } </script> <style> .cropper-content { display: flex; display: -webkit-flex; justify-content: flex-end; -webkit-justify-content: flex-end; } .cropper-content .cropper { width: 350px; height: 300px; } .cropper-content .show-preview { flex: 1; -webkit-flex: 1; display: flex; display: -webkit-flex; justify-content: center; -webkit-justify-content: center; overflow: hidden; border: 1px solid #cccccc; background: #cccccc; margin-left: 40px; } .preview { overflow: hidden; border: 1px solid #000000; background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC"); } .footer-btn { margin-top: 30px; display: flex; display: -webkit-flex; justify-content: flex-end; -webkit-justify-content: flex-end; } .footer-btn .scope-btn { width: 250px; display: flex; display: -webkit-flex; justify-content: space-between; -webkit-justify-content: space-between; } .footer-btn .upload-btn { flex: 1; -webkit-flex: 1; display: flex; display: -webkit-flex; justify-content: center; -webkit-justify-content: center; } .footer-btn .btn { outline: none; display: inline-block; line-height: 1; white-space: nowrap; cursor: pointer; -webkit-appearance: none; text-align: center; -webkit-box-sizing: border-box; box-sizing: border-box; outline: 0; margin: 0; -webkit-transition: 0.1s; transition: 0.1s; font-weight: 500; padding: 8px 15px; font-size: 12px; border-radius: 3px; color: #fff; background-color: #67c23a; border-color: #67c23a; } </style>
3.上傳組件
單張上傳要在el-upload加上list-type="picture"屬性json
<el-upload class="avatar-uploader" style="width: 150px;height: 150px;" action="http://upload-z2.qiniup.com/" :auto-upload="false" :show-file-list="false" list-type="picture" :on-change="handleCrop" :http-request="upload"> <img class="up-img" v-if="groupForm.logoUrl" :src="groupForm.logoUrl"> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload>
updated生命週期加上如下代碼後端
updated () { if (this.$refs.vueCropper) { this.$refs.vueCropper.Update() } },
4.el-upload的on-change事件函數 handleCrop方法app
handleCrop(file, files) { // 點擊彈出剪裁框 this.cropperModel = true this.file = file this.upPost.key = file.name },
5.自定義el-upload上傳方法 upload方法
使用原生xhr上傳base64格式圖片到七牛,this.upPost.key和than.upPost.token分別是上傳文件名和七牛token,七牛token是從後端獲取的token函數
upload(data) { let than = this //截取base64 data = data.substring(22); let timestamp = Date.parse(new Date()); let key = timestamp + this.upPost.key let url = "http://upload-z2.qiniup.com/putb64/" + this.fileSize(data) + '/key/' + this.baseCode64(key); let xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState == 4) { let json = JSON.parse(xhr.responseText) than.groupForm.logoUrl = '七牛資源路徑' + json.key than.$message.success('上傳成功!') than.cropperModel = false } } xhr.open("POST", url, true); xhr.setRequestHeader("Content-Type", "application/octet-stream"); xhr.setRequestHeader("Authorization", "UpToken " + than.upPost.token); xhr.send(data); },
6.工具方法工具
//計算文件大小函數 fileSize(str) { let fileSize; if (str.indexOf('=') > 0) { let indexOf = str.indexOf('='); str = str.substring(0, indexOf); //把末尾的’=‘號去掉 } fileSize = parseInt(str.length - (str.length / 8) * 2); return fileSize; }, //轉換base64函數 baseCode64(input){ var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = this._utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4); } return output; }, //字節轉換 _utf8_encode(string) { string = string.replace(/\r\n/g,"\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; },