移動端頭像裁剪上傳

作移動端的時候,各位可能會遇到用戶上傳頭像,而前端的input:file,若是上傳正方形圖片還好,可是若是是矩形的話,在頭像的顯示這塊,就會顯的很不和諧。javascript

前端根據矩形的圖片,實現裁剪成正方形,並把裁剪後的文件上傳給後臺。html

1.初始頁面以下,點擊中間默認頭像觸發input:file點擊,彈出選擇文件框.前端




2.開始選擇照片,以下,爲選擇的照片,和選擇照片後的效果圖:
java



3.此時圖片的高度大於寬度,所以能夠上下滾動,選擇想要裁剪的範圍,而後點擊選取(下圖從地鐵導航處開始裁剪)。
canvas



此時,已經裁剪到正方形圖片,能夠從新拍攝或者上傳給後臺。
ide

下面是代碼


<template> <div class="photo-panel"> <back-btn/> <h3 class="tc">頭像</h3> <div class="photo-box bg-w"> <div class="license-box"> <div class="bus-license img-box"> <img ref="avatar_img" src="@/assets/avater.png" class="avatar" alt="頭像" @click="upload('avatar')"> <input ref="avatar" :accept="accept" type="file" name="" class="hide" @change="setPhoto"> </div> <p v-show="!avatar" class="lh-3 tc ">點擊拍攝頭像</p> <p v-show="avatar" class="lh-3 tc"><span class="f-blue" @click="$refs.avatar.click();currentType='avatar'">從新拍攝 </span> 頭像 </p> </div> </div> <div v-show="show_clip_img" class="clip_img_box"> <div class="shade top_shade"/> <div ref="clip_img" class="clip_img"> <img ref="clip_avatar" :class="avater_width_larger_height? 'w_l_h' : ''" src="" class="clip_avatar" alt=""> </div> <div class="shade bottom_shade"> <div> <div class="cancel_clip clip_btn fl" @click="cancelClip">取消</div> <div class="check_clip clip_btn fr" @click="checkClip">選取</div> </div> </div> </div> <van-button type="info" size="large" class="upload_btn pdbox mgbox bottom-btn" @click="submit">上傳照片</van-button> </div></template>複製代碼

第一步:
 //用戶上傳圖片後觸發file:onchange setPhoto(e) {//對圖片尺寸作出限制並設置壓縮尺寸 let file = e.target.files[0]; if (!file) return '' //大於6兆,不容許上傳 if (file.size > this.maxSize * 6) {//this.maxSize 爲1024 * 1024 return this.$toast('圖片過大,不能大於6M'); } if (file.size > this.maxSize * 3) { this.ratio = 0.5;//ratio爲縮小比率,下面canvas壓縮要用 } else if (file.size > this.maxSize * 4) { this.ratio = 0.4; } Lrz(file, { width: 1024, quality: this.ratio })//這裏使用Lrz,防止圖片選中後旋轉,若是沒有這個問題,也能夠不使用Lrz,直接把this.file轉換爲bolb對象的圖片顯示出來 .then((rst) => { this.$refs['clip_avatar'].src = rst.base64;//須要裁剪的圖片賦值給clip_avatar this.$nextTick(() => {//引入圖片後獲取圖片的寬度是否大於高度 this.avater_width_larger_height = this.$refs['clip_avatar'].height < this.$refs['clip_avatar'].width }) this.show_clip_img = true;//展現裁剪圖片頁面 return rst; }).then((rst) => { this.file= rst.file; }) },複製代碼

checkClip(e) {//滑動圖片適合位置後點擊選取觸發該方法,獲取滾動的x和Y的距離傳給setPhotoCanvas,以便canvas裁剪。 let sTop = this.$refs['clip_img'].scrollTop; let sLeft = this.$refs['clip_img'].scrollLeft; this.setPhotoCanvas(sTop, sLeft); },複製代碼

setPhotoCanvas(sTop, sLeft) {//跟進圖片x和Y方向滾動的距離,從而裁剪相應的圖片 let reader = new FileReader(); reader.onloadend = (e) => { let img = new Image(); img.onload = () => { let w = Math.min(this.maxWidth, img.width); let h = img.height * (w / img.width); let [sw, sh, short, sx, sy] = [img.width, img.height, 0, 0, 0, 0] console.log(document.body.clientWidth) if (w < h) { short = w; sy = (sTop / document.body.clientWidth) * img.width; //這裏頭像裁剪的時候,寬度爲屏幕的寬度,所以document.body.clientWidth。若是正方形的寬度不是屏幕寬度,這裏和sx的獲取都須要替換。 sh = sw; } else { //w>h short = h; sx = (sLeft / document.body.clientWidth) * img.height; sw = sh; } let canvas = document.createElement('canvas'); let ctx = canvas.getContext('2d'); // 設置 canvas 的寬度和高度 canvas.width = short; canvas.height = short; ctx.drawImage(img, sx, sy, sw, sh, 0, 0, short, short);//此方法最重要。 let base64 = canvas.toDataURL('image/png', this.ratio); // 插入到預覽區 this.$refs['avatar_img'].src = base64; this.show_clip_img = false;//把圖片裁剪頁面隱藏 this.file = this.dataURItoBlob(base64);//上傳時上傳這個東西就ok完成啦 console.log('size-------------------' + this.file.size) }; img.src = e.target.result; } reader.readAsDataURL(this.file);

    },複製代碼

dataURItoBlob: function(dataURI) {//把base64 的圖片uri轉換成二進制大對象,方便傳給後臺 // convert base64/URLEncoded data component to raw binary data held in a string let byteString; if (dataURI.split(',')[0].indexOf('base64') >= 0) { byteString = atob(dataURI.split(',')[1]); } else { byteString = unescape(dataURI.split(',')[1]); } // separate out the mime component let mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; // write the bytes of the string to a typed array 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 }); },複製代碼

setPhoto(e) {      let _this = this;      let file = e.target.files[0];      if (!file) return ''      //大於6兆,不容許上傳 if (file.size > this.maxSize * 6) { this.$toast('圖片過大,不能大於6M') return; } if (file.size > this.maxSize * 3) { this.ratio = 0.5; } else if (file.size > this.maxSize * 4) { this.ratio = 0.4; } Lrz(file, { width: 1024, quality: this.ratio }) .then((rst) => { // 把處理的好的圖片給用戶看看唄 // _this.$refs['avatar_img'].src = rst.base64; _this.$refs['clip_avatar'].src = rst.base64; _this.$nextTick(() => { _this.avater_width_larger_height = _this.$refs['clip_avatar'].height < _this.$refs['clip_avatar'].width }) _this.show_clip_img = true; return rst; }).then((rst) => { this[this.currentType] = rst.file; }) },複製代碼

這就已經大功告成啦,難點主要在獲取圖片滾動的距離後怎麼進行裁剪,this

ctx.drawImage(img, sx, sy, sw, sh, 0, 0, short, short);
spa

這行代碼,起着決定做用,img表明圖片文件,sx表明souce-x,既源圖片X軸從哪裏開始裁剪,sy表明souce-y,既源圖片Y軸從哪裏開始裁剪。3d

sw和sh分別表明,souce-width和souce-height。即原圖片x和Y方向裁剪的寬度和長度。code

0,0表明從新畫的image從canvas的0,0座標開始。既左上角。

short,short,short來源於圖片長度和寬度的比較,獲取較短的那個邊的長度。

而後就能夠根據這幾個參數canvas繪畫出對應的正方形圖片了。

最後因爲上傳base64較大,就改成二進制上傳,轉換爲bolb對象。

相關文章
相關標籤/搜索