头像修改功能 包含ios旋转图片 但是旋转后没遮罩, 正常图片可以显示遮罩 宽高不规则图片没做控制 遮罩框可以拖动
https://blog.csdn.net/wk767113154/article/details/77989544 参考资料
<template> <div id="profile" class="has-top"> <!-- <v-header title="我的资料"></v-header> --> <!-- <div class="section" @click="changeAvatar()"> <mt-cell class="no-line" title="头像" is-link> <v-image :src="userInfo.avatar" avatar class="circle60 mt_mb_10" /> </mt-cell> </div> --> <div class="box"> <div ref="cvs"> <canvas id="canvas" width="0" height="0"></canvas> </div> <div class="btn" @click="confirmBtn()"> 确认 </div> <div style="display:none"> <canvas id="preview" :width="cvw" :height="cvw"></canvas> </div> </div> <div class="section"> <div class="head-pic"> <span class="left">头像</span> <input type="file" class="tx-file" @change="handleFileChange" accept="image/*"> <div v-if="txStatus"> <div v-if="!defaultPic"> <img src="../../assets/images/account/default_avatar.png" alt=""> </div> <div v-else> <img :src="defaultPic" alt=""> </div> </div> <div v-else> <img :src="successPic" alt=""> </div> <!-- <v-image :src="userInfo.avatar" avatar class="mt_mb_10" /> --> </div> <mt-cell title="昵称" :value="nickName" is-link :to="{path:'/user/edit-nickname'}" /> <mt-cell title="管理收货地址" is-link :to="{path:'/address/list?form=profile'}" /> <!-- <div class="mint-cell" @click="bindPhone()"> <span class="mint-cell-mask"></span> <div class="mint-cell-wrapper"> <div class="mint-cell-title"><span class="mint-cell-text">{{userInfo.phone ? '当前' : ''}}绑定手机</span></div> <div class="mint-cell-value is-link"><span>{{phone}}</span></div> </div> <i class="mint-cell-allow-right"></i> </div> <mt-cell :title="state.password + '密码'" :value="state.text" is-link :to="{path: state.url}" /> --> <!-- <mt-cell title="管理收货地址" is-link :to="{path:'/address/list'}" /> --> <!-- <div class="mint-cell" @click="bindBank()"> <span class="mint-cell-mask"></span> <div class="mint-cell-wrapper"> <div class="mint-cell-title"><span class="mint-cell-text">绑定银行卡</span></div> <div class="mint-cell-value is-link"><span>{{bank.accountStatus||'未绑定'}}</span></div> </div> <i class="mint-cell-allow-right"></i> </div> --> </div> <div class="section mt10"> <template v-if="invitor"> <mt-cell title="我的分享人" :to="{path:'/user/invitor'}" :value="invitor.nickName" is-link/> </template> <template> <mt-cell title="账户与安全" :to="{path:'/account'}" is-link/> </template> </div> <div class="section mt10"> <!-- <div class="mint-cell" @click="onContact()"> <span class="mint-cell-mask"></span> <div class="mint-cell-wrapper"> <div class="mint-cell-title"><span class="mint-cell-text">联系客服</span></div> </div> <i class="mint-cell-allow-right"></i> </div> --> <mt-cell title="版本号" value="1.0.1"/> </div> <div class="logout" @click="logout()"> 退出登录 </div> </div> </template> <script type="text/javascript"> import Config from '../../config.js'; import UserService from '../../services/UserService.js'; import BankService from '../../services/BankService.js'; import WeChatService from '../../services/WeChatService.js'; import CsService from '../../services/CsService.js'; import { MessageBox } from 'mint-ui'; import axios from 'axios'; import EXIF from 'exif-js' export default { initWechat: true, data() { return { defaultPic: '', successPic: '', txStatus: true, phone: '', nickName: '', userInfo: {}, invitor: undefined, bank: {}, config: null, hasPassword: false, state: { password: '', text: '', url: '/user/edit-password' }, canvas: null, ctx: null, preview: null, ctxpreview: null, img: null, btn: null, imgdata: null, //选中区域内容 point: null, flag: false, cvw:0, orientation: null, isIos: false, lastX: null, lastY: null, disX: null, disY: null } }, methods: { logout() { UserService.logout(this); }, handleFileChange(ev) { // WeChatService.uploadImage(this, (image) => { // UserService.updateAvatar(this, image, () => { // this.showMessage('更新头像成功'); // this.userInfo.avatar = image; // }); // }); var _this = this // let params = new FormData() // let imgFile = ev.target.files[0] // params.append('file', imgFile); // params.append('version', '1.0'); /* 图片裁剪 */ _this.img = new Image() var reader = new FileReader() var file = ev.target.files[0] if(!file) { return false } document.querySelector('.box').style.display = 'block' reader.readAsDataURL(file); reader.onload = function(e) { console.log(_this.img) _this.img.src = e.target.result _this.img.onload = function() { _this.orientation = null EXIF.getData(_this.img, function () { EXIF.getAllTags(this); _this.orientation = EXIF.getTag(this, "Orientation");// 获取方向角 var degree = 0, drawWidth = _this.img.width, drawHeight = _this.img.height, width, height; //以下改变一下图片大小 var cliWidth = Math.floor(document.documentElement.clientWidth) var cliHeight = Math.floor(document.documentElement.clientHeight) var maxSide = Math.max(drawWidth, drawHeight); var tarSize = cliHeight // if(drawWidth > drawHeight) { // tarSize = cliWidth // }else { // tarSize = cliHeight // } if (maxSide > tarSize) { var minSide = Math.min(drawWidth, drawHeight); minSide = minSide / maxSide * tarSize; maxSide = tarSize; if (drawWidth > drawHeight) { drawWidth = maxSide; drawHeight = minSide; } else { if(minSide > cliWidth) { drawHeight = cliWidth * drawHeight / drawWidth drawWidth = cliWidth }else{ drawWidth = minSide; drawHeight = maxSide; } } } else{ drawHeight = cliWidth * drawHeight / drawWidth drawWidth = cliWidth } _this.canvas.width = width = drawWidth; _this.canvas.height = height = drawHeight; switch (_this.orientation) {//横屏竖屏转化 //横屏拍摄,此时home键在左侧 case 3: degree = 180; drawWidth = -width; drawHeight = -height; break; //竖屏拍摄,此时home键在下方(正常拿手机的方向) case 6: _this.canvas.width = height; _this.canvas.height = width; console.log(_this.canvas.width,_this.canvas.height) degree = 90; drawWidth = width; drawHeight = -height; break; //竖屏拍摄,此时home键在上方 case 8: _this.canvas.width = height; _this.canvas.height = width; degree = 270; drawWidth = -width; drawHeight = height; break; } _this.ctx.rotate(degree* Math.PI/180); _this.ctx.clearRect(0,0,_this.canvas.width,_this.canvas.height); _this.ctx.drawImage(_this.img, 0, 0, drawWidth, drawHeight); _this.ctx.save() _this.ctx.fillStyle = 'rgba(0,0,0,0.5)' _this.ctx.fillRect(0,0,_this.canvas.width,_this.canvas.height) _this.ctx.restore() // if(cliHeight > drawHeight) { // _this.$refs.cvs.style.marginTop = (cliHeight-drawHeight)/2 + 'px' // } // _this.drawcenter() _this.ctx.save() _this.ctx.fillStyle = 'rgba(0,0,0,0.5)' _this.ctx.fillRect(0,0,_this.canvas.width,_this.canvas.height) _this.ctx.restore() _this.lastX = _this.canvas.width / 2 _this.lastY = _this.canvas.height / 2 _this.drawtop((_this.canvas.width - _this.cvw)/2,(_this.canvas.height - _this.cvw)/2,drawWidth,drawHeight) // 移动canvas _this.canvas.ontouchstart = function(e) { e.preventDefault(); if(e.touches.length == 1) { _this.flag = true _this.point = _this.windowToCanvas(_this.canvas,e.touches[0].clientX,e.touches[0].clientY) _this.disX = _this.point.x - _this.lastX _this.disY = _this.point.y - _this.lastY _this.point.x = _this.point.x - _this.disX _this.point.y = _this.point.y - _this.disY _this.ctx.clearRect(0,0,_this.canvas.width,_this.canvas.height); _this.ctx.drawImage(_this.img, 0, 0, drawWidth, drawHeight); _this.ctx.save() _this.ctx.fillStyle = 'rgba(0,0,0,0.5)' _this.ctx.fillRect(0,0,_this.canvas.width,_this.canvas.height) _this.ctx.restore() _this.drawtop(_this.point.x-(_this.cvw/2),_this.point.y-(_this.cvw/2),drawWidth,drawHeight) } } _this.canvas.ontouchmove = function(e) { e.preventDefault(); if(_this.flag) { _this.point = _this.windowToCanvas(canvas,e.changedTouches[0].clientX,e.changedTouches[0].clientY) // console.log(point.x + ' - ' + point.y) _this.lastX = _this.point.x - _this.disX _this.lastY = _this.point.y - _this.disY _this.point.x = _this.point.x - _this.disX _this.point.y = _this.point.y - _this.disY _this.ctx.clearRect(0,0,_this.canvas.width,_this.canvas.height); _this.ctx.drawImage(_this.img, 0, 0, drawWidth, drawHeight); _this.ctx.save() _this.ctx.fillStyle = 'rgba(0,0,0,0.5)' _this.ctx.fillRect(0,0,_this.canvas.width,_this.canvas.height) _this.ctx.restore() _this.drawtop(_this.point.x-(_this.cvw/2),_this.point.y-(_this.cvw/2),drawWidth,drawHeight) } } _this.canvas.ontouchend = function(e) { //将鼠标状态释放 _this.flag = false _this.point.x = _this.point.x _this.point.y = _this.point.y _this.imgdata = _this.ctx.getImageData(_this.point.x-(_this.cvw/2),_this.point.y-(_this.cvw/2),_this.cvw,_this.cvw) _this.ctxpreview.putImageData(_this.imgdata,0,0) } // if(drawWidth < cliWidth) { // if(drawWidth < drawHeight) { // //_this.drawtop(drawWidth/2-_this.cvw/2,drawHeight/2-_this.cvw/2,drawWidth,drawHeight) // _this.ctx.save() // _this.ctx.beginPath() // console.log(0,drawHeight/2-drawWidth/2,drawWidth,drawWidth) // _this.ctx.rect(0,drawHeight/2-drawWidth/2,drawWidth,drawWidth) // _this.ctx.closePath() // _this.ctx.clip() // //在该图层绘制原图 // _this.ctx.drawImage(_this.img,0, 0,drawWidth,drawHeight) // console.log(0,drawHeight/2-drawWidth/2,drawWidth,drawWidth) // _this.imgdata = _this.ctx.getImageData(0,drawHeight/2-drawWidth/2,drawWidth,drawWidth) // console.log(0,drawHeight/2-drawWidth/2,drawWidth,drawWidth) // _this.ctxpreview.putImageData(_this.imgdata,0,0) // _this.ctx.restore() // }else{ // _this.ctx.save() // _this.ctx.beginPath() // _this.ctx.rect(drawWidth/2-drawHeight/2,0,drawHeight,drawHeight) // _this.ctx.closePath() // _this.ctx.clip() // //在该图层绘制原图 // _this.ctx.drawImage(_this.img,0, 0,drawWidth,drawHeight) // _this.imgdata = _this.ctx.getImageData(drawWidth/2-drawHeight/2,0,drawHeight,drawHeight) // _this.ctxpreview.putImageData(_this.imgdata,0,0) // _this.ctx.restore() // } // }else{ // _this.drawtop(cliWidth/2-(_this.cvw/2),cliHeight/2-(_this.cvw/2),drawWidth,drawHeight) // } //_this.drawtop((drawWidth/2-_this.cvw/2),(drawHeight/2-_this.cvw/2),drawWidth,drawHeight) }) // _this.canvas.width = Math.floor(document.documentElement.clientWidth) // _this.canvas.height = Math.floor(document.documentElement.clientHeight) // _this.ctx.clearRect(0,0,_this.canvas.width,_this.canvas.height); // //第一层将图像绘制到canvas // _this.ctx.drawImage(_this.img,(_this.canvas.width-_this.img.width)/2,(_this.canvas.height-_this.img.height)/2,_this.img.width,_this.img.height) } // console.dir(e.target.result) } }, confirmBtn() { var _this = this document.querySelector('.box').style.display = 'none' //console.log(preview.toDataURL("image/png")) let params = new FormData() let dataURL = this.preview.toDataURL("image/png") let blob = this.dataURItoBlob(dataURL); params.append('file', blob); params.append('version', '1.0'); axios.post(process.env.BASE_API + 'ddmallapi/upload/uploadImage', params, { headers:{'Content-Type':'multipart/form-data'} }).then(function(res) { console.log(res) if(res.data.data.imgUrl) { _this.txStatus = false _this.successPic = res.data.data.imgUrl; let paramSave = new FormData() paramSave.append('headImage',res.data.data.imgUrl) axios.post(process.env.BASE_API + 'ddmallapi/user/changeHead?version=1.0', paramSave, { headers:{'Content-Type':'application/x-www-form-urlencoded'} }).then(function(res) { // if(res.data.code === 0) { // UserService.getInfo(_this,(res) => { // console.log(res.data.data.headImage) // }); // } }).catch(function(err) { console.log(err) }) }else{ _this.showError(res.data.message); } }).catch(function(err) { console.log(err) }) }, onContact() { CsService.showCsView(); }, bindPhone() { if (!this.hasPassword) { let toast = Config.toast.filter(item => item.id == 'noLogin')[0]; this.showConfirms(toast.title, toast.message, () => { this.$router.push({ path: '/find-password', query: { redirect: location.pathname, set: true } }) }, null, '去设置', '取消'); return; } if (!this.userInfo.phone) { this.$router.push({ path: '/bind/phone' }); return; } this.$router.push({ path: '/user/edit-phone' }); }, checkPassword() { UserService.checkPassword(this, (res) => { if (res.code == 1) { this.hasPassword = true; this.state.password = '修改'; } else { this.state.password = '设置'; this.state.text = '去设置'; this.state.url = '/find-password' this.state.url += '?set=true&redirect='+location.pathname } }) }, drawcenter() { //第二层 在图片上绘制半透明遮罩 this.ctx.save() this.ctx.fillStyle = 'rgba(0,0,0,0.5)' this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height) this.ctx.restore() }, drawtop(x,y,drawWidth,drawHeight){ //第三层 局部高亮 this.ctx.save() this.ctx.beginPath() this.ctx.rect(x,y,this.cvw,this.cvw) this.ctx.closePath() this.ctx.clip() //在该图层绘制原图 this.ctx.drawImage(this.img,0, 0,drawWidth,drawHeight) this.imgdata = this.ctx.getImageData(x,y,this.cvw,this.cvw) this.ctxpreview.putImageData(this.imgdata,0,0) this.ctx.restore() }, windowToCanvas(element,x,y) { var box = element.getBoundingClientRect(); var cx = x - box.left var cy = y - box.top return {x:cx,y:cy} }, dataURItoBlob(dataURI) { var byteString = atob(dataURI.split(',')[1]); var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; var ab = new ArrayBuffer(byteString.length); var ia = new Uint8Array(ab); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ab], {type: mimeString}); } }, created() { this.checkPassword(); }, mounted() { this.canvas = document.getElementById('canvas') this.preview = document.getElementById('preview') this.ctx = this.canvas.getContext('2d') this.ctxpreview = this.preview.getContext('2d') this.btn = document.querySelector('.btn') this.cvw = 300 this.$root.$on('user.info.update', (userInfo) => { console.log(userInfo) this.userInfo = userInfo.data; if (this.userInfo.phone) { this.phone = this.userInfo.phone.substr(0, 3) + '****' + this.userInfo.phone.substr(7); } if (this.userInfo.nickName && this.userInfo.nickName.length > 7) { this.nickName = this.userInfo.nickName.substr(0, 7) + '...'; } else { this.nickName = this.userInfo.nickName } }); UserService.getInfo(this, (res) => { if(res.headImage) { this.defaultPic = res.headImage } }); UserService.getInvitor(this, (data) => { this.invitor = data; }, () => {}); } } </script> <style> .logout { margin-top: 20px; height: 45px; text-align: center; background: #fff; line-height: 45px; font-size: 16px; color: #ff4646; } .mint-cell { min-height: 44px; } .mint-cell-text { font-size: 16px !important } .mint-cell-value > span { font-size: 16px !important } .head-pic { padding: 0 15px; position: relative; height: 50px; border-bottom: 1px solid #f0f0f0; } .head-pic .tx-file{ position:absolute; left: 0; top: 0; width: 100%; height: 50px; opacity: 0; } .head-pic img { margin-top: 5px; float:right; width: 40px; height: 40px; border-radius: 50%; } .head-pic .left{ float: left; font-size: 16px !important; line-height: 50px; } .user-center-header .header-left{ margin-left: 0.7rem; } </style> <style scoped> .box{ position: absolute; left: 0; top: 0; right: 0; bottom: 0; z-index: 111; background: #333; display: none; overflow: hidden; text-align:center; } .box .btn { position: absolute; width: 60px; height: 30px; right: 5px; top: 5px; z-index: 111; background: #fff; line-height: 30px; text-align: center; border-radius: 5px; } .box canvas{ } </style>