在項目遇到了如題所述的需求,很是折騰了一番。畢竟寫前端和移動端native是不一樣的!!現在進入正題,把dom部分擺出來:
<div>
<vueCropper style="width: 90%;height: 260px;margin-left: 5%;margin-top: 15px"
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>
<input style="margin-left: 20px;margin-top: 15px" id="file_input" type="file" capture="camera"/>
<div class="btn" @click="compressImg()">上傳頭像</div>
</div>
可以看到,在dom中我們使用了vueCropper這個元件。這個是今天的主角,圖檔裁剪控件。上github:
https://github.com/xyxiao001/vue-cropper
安裝:$:npm install vue-cropper -s
vue-cropper元件的屬性:
名稱 | 功能 | 預設值 | 可選值 |
img | 裁剪圖檔的位址 | 空 | url 位址 || base64 || blob |
outputSize | 裁剪生成圖檔的品質 | 1 | 0.1 - 1 |
outputType | 裁剪生成圖檔的格式 | jpg (jpg 需要傳入jpeg) | jpeg || png || webp |
info | 裁剪框的大小資訊 | true | true || false |
canScale | 圖檔是否允許滾輪縮放 | true | true || false |
autoCrop | 是否預設生成截圖框 | false | true || false |
autoCropWidth | 預設生成截圖框寬度 | 容器的80% | 0~max |
autoCropHeight | 預設生成截圖框高度 | 容器的80% | 0~max |
fixed | 是否開啟截圖框寬高固定比例 | true | true | false |
fixedNumber | 截圖框的寬高比例 | [1 : 1] | [寬度 : 高度] |
full | 是否輸出原圖比例的截圖 | false | true | false |
fixedBox | 固定截圖框大小 不允許改變 | false | true | false |
canMove | 上傳圖檔是否可以移動 | true | true | false |
canMoveBox | 截圖框能否拖動 | true | true | false |
original | 上傳圖檔按照原始比例渲染 | false | true | false |
centerBox | 截圖框是否被限制在圖檔裡面 | false | true | false |
high | 是否按照裝置的dpr 輸出等比例圖檔 | true | true | false |
infoTrue | true 為展示真實輸出圖檔寬高 false 展示看到的截圖框寬高 | false | true | false |
maxImgSize | 限制圖檔最大寬度和高度 | 2000 | 0-max |
内置方法通過this.$refs.cropper 調用:
this.$refs.cropper.startCrop() 開始截圖
this.$refs.cropper.stopCrop() 停止截圖
this.$refs.cropper.clearCrop() 清除截圖
this.$refs.cropper.changeScale() 修改圖檔大小 正數為變大 負數變小
this.$refs.cropper.getImgAxis() 擷取圖檔基于容器的坐标點
this.$refs.cropper.getCropAxis() 擷取截圖框基于容器的坐标點
this.$refs.cropper.goAutoCrop 自動生成截圖框函數
this.$refs.cropper.rotateRight() 向右邊旋轉90度
this.$refs.cropper.rotateLeft() 向左邊旋轉90度
圖檔加載的回調 imgLoad 傳回結果success, error
// 擷取截圖的base64 資料
this.$refs.cropper.getCropData((data) => {
// do something
console.log(data)
})
// 擷取截圖的blob資料
this.$refs.cropper.getCropBlob((data) => {
// do something
console.log(data)
})
好了,vue-cropper就介紹到這裡。詳細的可以去github裡面看。
現在我們看看 input type='file' 通過Input我們可以得到選擇的圖檔,直接放入vueCropper中來:
mounted(){
let _self = this;
$("input[type='file']").change(function(){
Indicator.open();
let file = this.files[0];
if (window.FileReader) {
let reader = new FileReader();
reader.readAsDataURL(file);
//監聽檔案讀取結束後事件
reader.onloadend = function (e) {
_self.option.img = e.target.result;
Indicator.close();
};
}
});
},
這裡使用jquery來擷取選取的圖檔,而且要放在mounted()生命周期鈎子中。不能放入之前的生命的鈎子中。
現在調用裁剪方法:
compressImg:function () {
let _self = this;
this.$refs.cropper.startCrop();
this.$refs.cropper.getCropData((data) => {
let file = _self.convertBase64UrlToBlob(data);
file.name = 'head.jpg';
_self.uploadAction(file);
})
},
這裡我們用得到的BASE64資料轉換成Blob資料,直接把Blob當做file來這裡,不要忘記設定file.name。不然上傳檔案的時候是會出錯的!!得到資料之後,就開始上傳了,調用你自己的ajax方法吧!這裡我們使用axios來做圖檔上傳:
uploadAction:function (file) {
Indicator.open();
let _self = this;
let param = new FormData(); // 建立form對象
param.append('pics', file, file.name); // 通過append向form對象添加資料
let config = {
headers: {'Content-Type': 'multipart/form-data'}
};
// 添加請求頭
this.$axios.post('/appapi/user/i/up-headimg/pics', param, config)
.then(response => {
if(response.status == 200){
let data = response.data;
Indicator.close();
//出來伺服器的傳回資料
}
})
.catch(error=>{
Indicator.close();
console.log(error);
})
},
完整代碼如下:
<template>
<div>
<vueCropper style="width: 90%;height: 260px;margin-left: 5%;margin-top: 15px"
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>
<input style="margin-left: 20px;margin-top: 15px" id="file_input" type="file" capture="camera"/>
<div class="btn" @click="compressImg()">上傳頭像</div>
<span>{{status}}</span>
</div>
</template>
<script>
import { MessageBox, Indicator,Toast,Actionsheet } from "mint-ui";
import $ from 'jquery'
import VueCropper from "vue-cropper"
export default {
name: "ImageUpload",
components:{
VueCropper,
MessageBox,
Indicator,
Toast,
},
mounted(){
let _self = this;
$("input[type='file']").change(function(){
Indicator.open();
let file = this.files[0];
if (window.FileReader) {
let reader = new FileReader();
reader.readAsDataURL(file);
//監聽檔案讀取結束後事件
reader.onloadend = function (e) {
_self.option.img = e.target.result;
Indicator.close();
};
}
});
},
data(){
return{
option: {
img: "", //裁剪圖檔的位址
info: true, //裁剪框的大小資訊
outputSize: 0.7, // 裁剪生成圖檔的品質
outputType: 'jpeg', //裁剪生成圖檔的格式
canScale: true, // 圖檔是否允許滾輪縮放
autoCrop: true, // 是否預設生成截圖框
autoCropWidth: 100, // 預設生成截圖框寬度
autoCropHeight: 100, // 預設生成截圖框高度
fixed: false, //是否開啟截圖框寬高固定比例
fixedNumber: [4, 4] //截圖框的寬高比例
},
status:'',
headImg:''
}
},
methods:{
showToast:function (msg) {
Toast({
message: msg,
position: 'bottom',
duration: 2000
})
},
compressImg:function () {
let _self = this;
this.$refs.cropper.startCrop();
this.$refs.cropper.getCropData((data) => {
let file = _self.convertBase64UrlToBlob(data);
file.name = 'head.jpg';
_self.uploadAction(file);
})
},
uploadAction:function (file) {
Indicator.open();
let _self = this;
let param = new FormData(); // 建立form對象
param.append('pics', file, file.name); // 通過append向form對象添加資料
let config = {
headers: {'Content-Type': 'multipart/form-data'}
};
// 添加請求頭
this.$axios.post('/appapi/user/i/up-headimg/pics', param, config)
.then(response => {
if(response.status == 200){
let data = response.data;
Indicator.close();
//處理伺服器傳回資料
}
})
.catch(error=>{
Indicator.close();
console.log(error);
})
},
// 将base64的圖檔轉換為file檔案
convertBase64UrlToBlob(urlData) {
let bytes = window.atob(urlData.split(',')[1]);//去掉url的頭,并轉換為byte
//處理異常,将ascii碼小于0的轉換為大于0
let ab = new ArrayBuffer(bytes.length);
let ia = new Uint8Array(ab);
for (var i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob([ab], { type: 'image/jpeg' });
},
}
}
</script>
<style scoped>
.btn{
width: 80%;
height: 40px;
margin-top: 50px;
color: white;
border-radius: 20px;
background-color: #3377ff;
display: flex;
align-items: center;
justify-content: center;
margin-left: 10%;
}
</style>