移动端头像裁剪上传

作移动端的时候,各位可能会遇到用户上传头像,而前端的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对象。

相关文章
相关标签/搜索