console
var app = new Vue({
el: '#app',
data: function(){
return {
uploadData:{
uploadImgBox:false,
anewImgBox:false,
negativeImg:new Image(),
handleImg:new Image(),
box:{
width:360,
height:360
},
filmCanvas:{
width:0,
height:0,
styleObj:{
top:0,
left:0
}
},
drawCanvas:{
width:0,
height:0,
ctx:null
},
circle:{
x:0,
y:0,
r:50
},
handleImg:new Image(),
handle:{
x:0,
y:0,
r:10
},
handleFlag:false,
coord:{
x:0,
y:0
},
flag:false,
dragMouse:false,
zoomMouse:false,
imgData:'http://p4mxf46uj.bkt.clouddn.com/protrait/default_protrait.png',
pos:0,
}
}
},
created:function(){
this.uploadData.handleImg.src = 'http://p4mxf46uj.bkt.clouddn.com/userhandle.png';
},
methods:{
showUpload:function(){
this.uploadData.uploadImgBox = true;
this.uploadData.anewImgBox = false;
},
closeUpload:function(){
this.uploadData.uploadImgBox = false;
},
uploadPortrait:function(e){
let self = this;
self.uploadData.anewImgBox = true;
if(!e.target.files || !e.target.files[0]) return ;
let filmCanvas = document.getElementById('filmCanv');
let filmCtx = filmCanvas.getContext('2d');
if(!filmCtx) alert('浏览器不支持canvas,请升级浏览器');
let windowURL = window.URL || window.webkitURL;
let imgsrc = windowURL.createObjectURL(e.target.files[0]);
self.uploadData.negativeImg.onload = function(){
let imgW = self.uploadData.negativeImg.width;
let imgH = self.uploadData.negativeImg.height;
let tmpCan = isUpImgWH(imgW,imgH,self.uploadData.box);
filmCanvas.width = tmpCan.width;
filmCanvas.height = tmpCan.height;
self.uploadData.filmCanvas.styleObj.top = tmpCan.top;
self.uploadData.filmCanvas.styleObj.left = tmpCan.left;
self.uploadData.filmCanvas.width = filmCanvas.width;
self.uploadData.filmCanvas.height = filmCanvas.height;
filmCtx.drawImage(self.uploadData.negativeImg,0,0,filmCanvas.width,filmCanvas.height);
let drawCanvas = document.getElementById('drawCanv');
drawCanvas.width = self.uploadData.filmCanvas.width;
drawCanvas.height = self.uploadData.filmCanvas.height;
let drawCtx = drawCanvas.getContext('2d');
self.uploadData.drawCanvas.ctx = drawCtx;
self.uploadData.drawCanvas.width = self.uploadData.filmCanvas.width;
self.uploadData.drawCanvas.height = self.uploadData.filmCanvas.height;
self.uploadData.circle.x = drawCanvas.width / 2;
self.uploadData.circle.y = drawCanvas.height / 2;
self.uploadData = clipProtrait(self.uploadData);
let imgData = previewProtrait(self.uploadData);
self.uploadData.imgData = imgData;
}
self.uploadData.negativeImg.src = imgsrc;
},
downPortrait:function(event){
let self = this;
self.uploadData.pos = 1;
let portraitCircle = getPointDistance(self.uploadData.circle,{x:event.offsetX,y:event.offsetY});
let handleCircle = getPointDistance(self.uploadData.handle,{x:event.offsetX,y:event.offsetY});
if(handleCircle){
self.uploadData.handleFlag = true;
}else if(portraitCircle){
self.uploadData.flag = true;
}
let ctx = self.uploadData.drawCanvas.ctx;
if(ctx && (handleCircle || portraitCircle)){
self.uploadData.coord.x = event.offsetX;
self.uploadData.coord.y = event.offsetY;
}
},
movePortrait:function(event){
let self = this;
let flag = self.uploadData.flag;
let handleFlag = self.uploadData.handleFlag;
let ctx = self.uploadData.drawCanvas.ctx;
let coord = self.uploadData.coord;
let portraitCircle = getPointDistance(self.uploadData.circle,{x:event.offsetX,y:event.offsetY});
let handleCircle = getPointDistance(self.uploadData.handle,{x:event.offsetX,y:event.offsetY});
if(handleCircle){
self.uploadData.zoomMouse = true;
self.uploadData.dragMouse = false;
}else if(portraitCircle){
self.uploadData.dragMouse = true;
self.uploadData.zoomMouse = false;
}else {
self.uploadData.dragMouse = false;
self.uploadData.zoomMouse = false;
};
if(ctx && handleFlag){
let direction = coord.x - event.offsetX;
if(direction > 0){
self.uploadData.pos--;
self.uploadData.circle.r -= Math.abs(self.uploadData.pos);
}else if(direction < 0){
self.uploadData.pos++;
self.uploadData.circle.r += Math.abs(self.uploadData.pos);
}else return ;
if(self.uploadData.circle.r < 30) self.uploadData.circle.r = 30;
if(self.uploadData.drawCanvas.width > self.uploadData.drawCanvas.height){
if(self.uploadData.circle.r > Math.floor(self.uploadData.drawCanvas.height /2) ) self.uploadData.circle.r = Math.floor(self.uploadData.drawCanvas.height /2);
}else if(self.uploadData.drawCanvas.height > self.uploadData.drawCanvas.width){
if(self.uploadData.circle.r > Math.floor(self.uploadData.drawCanvas.width /2) ) self.uploadData.circle.r = Math.floor(self.uploadData.drawCanvas.width /2);
}
self.uploadData = clipProtrait(self.uploadData);
let imgData = previewProtrait(self.uploadData);
self.uploadData.imgData = imgData;
}else if(ctx && flag){
let distanceX = self.uploadData.coord.x - event.offsetX;
let distanceY = self.uploadData.coord.y - event.offsetY;
self.uploadData.coord.x = event.offsetX;
self.uploadData.coord.y = event.offsetY;
self.uploadData.circle.x = self.uploadData.circle.x - distanceX;
self.uploadData.circle.y = self.uploadData.circle.y - distanceY;
self.uploadData = clipProtrait(self.uploadData);
let imgData = previewProtrait(self.uploadData);
self.uploadData.imgData = imgData;
}
},
upPortrait:function(event){
let self = this;
self.uploadData.pos = 0;
self.uploadData.flag = false;
self.uploadData.handleFlag = false;
},
saveUploadPortrait:function(){
let self = this;
$.post('url',{'img':self.uploadData.imgData},function(){
});
let formData = new FormData();
let imgBlog = convertBase64UrlToBlob(self.uploadData.imgData);
formData.append('file', imgBlog);
$.post('url',formData,function(){
});
}
}
});
function isUpImgWH(width,height,box){
let canW = null;
let canH = null;
let offsetTop = 0;
let offsetLeft = 0;
if(width > height){
canW = box.width;
canH = Math.floor((canW * height)/ width);
if(canH > box.width) canH = box.width;
if(canH == box.width) {
offsetTop = 0;
offsetLeft = 0;
}
if(canH < box.width){
offsetTop = Math.floor((box.width - canH)/2);
offsetLeft = 0;
}
}else{
canH = box.height;
canW = Math.floor((width * canH)/height);
if(canW > box.height) canW = box.height;
if(canW == box.height){
offsetTop = 0;
offsetLeft = 0;
}
if(canW < box.height){
offsetLeft = Math.floor((box.height - canW)/2);
offsetTop = 0;
}
}
return {
width:canW,
height:canH,
top:offsetTop + 'px',
left:offsetLeft + 'px'
}
}
function clipProtrait(data){
let canWidth = data.drawCanvas.width;
let canHeight = data.drawCanvas.height;
let point = data.circle;
let ctx = data.drawCanvas.ctx;
let radius = data.circle.r;
let handle = data.handle;
let handleImg = data.handleImg;
if(data.circle.x < radius) data.circle.x = radius;
if(data.circle.x > canWidth - radius) data.circle.x = canWidth - radius;
if(data.circle.y < radius) data.circle.y = radius;
if(data.circle.y > canHeight - radius) data.circle.y = canHeight - radius;
ctx.clearRect(0,0,canWidth,canHeight);
drawRect(ctx,0,0,canWidth,canHeight,0,0,'rgba(0,0,0,0.6)');
ctx.save();
ctx.beginPath();
ctx.arc(point.x,point.y,radius,0,Math.PI * 2);
ctx.clip();
ctx.drawImage(data.negativeImg,0,0,canWidth,canHeight);
ctx.restore();
ctx.save();
ctx.beginPath();
let tmpPonit = getCircle({x:point.x,y:point.y,r:radius},45);
ctx.arc(tmpPonit.x,tmpPonit.y,handle.r,0,Math.PI * 2);
data.handle.x = tmpPonit.x;
data.handle.y = tmpPonit.y;
ctx.fillStyle = '#fff';
ctx.fill();
ctx.clip();
ctx.restore();
ctx.drawImage(handleImg,tmpPonit.x - handle.r - 3,tmpPonit.y - handle.r - 3,26,26);
data.drawCanvas.ctx = ctx;
return data;
}
function drawRect(cxt,x,y,width,height,BW,BC,fillColor){
cxt.beginPath();
cxt.moveTo(x,y);
cxt.lineTo(x+width,y);
cxt.lineTo(x+width,y+height);
cxt.lineTo(x,y+height);
cxt.closePath();
cxt.lineWidth = BW;
cxt.fillStyle = fillColor;
cxt.strokeStyle = BC;
cxt.fill();
cxt.stroke();
}
function previewProtrait(data){
let imgW = data.negativeImg.width;
let imgH = data.negativeImg.height;
let point = data.circle;
let k = imgW / data.drawCanvas.width;
let sx = Math.floor((point.x - data.circle.r) * (imgW / data.drawCanvas.width)) ;
let sy = Math.floor((point.y - data.circle.r) * (imgH / data.drawCanvas.height));
let sW = Math.floor(2 * point.r * k);
let sH = Math.floor(2 * point.r * k);
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
canvas.width = 290;
canvas.height = 290;
drawRect(ctx,0,0,canvas.width,canvas.height,0,0,'#000');
ctx.drawImage(data.negativeImg,sx,sy,sW,sH,0,0,canvas.width,canvas.height);
let imgData = canvas.toDataURL("image/jpeg", 1);
return imgData;
}
function getCircle(circle,angle){
let obj = {};
obj.x = circle.x + circle.r * Math.cos(angle * Math.PI / 180);
obj.y = circle.y + circle.r * Math.sin(angle * Math.PI / 180);
return obj;
}
function getPointDistance(point1,point2){
let count = (point1.x * point1.x) -(2 * point1.x * point2.x) + (point2.x * point2.x) + (point1.y * point1.y) -(2 * point1.y * point2.y) + (point2.y * point2.y);
let tmpd1 = Math.sqrt( count);
if(point1.r > tmpd1){
return true;
}
return false;
}
function convertBase64UrlToBlob(urlData){
var bytes=window.atob(urlData.split(',')[1]);
var ab = new ArrayBuffer(bytes.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < bytes.length; i++) {
ia[i] = bytes.charCodeAt(i);
}
return new Blob( [ab] , {type : 'image/png'});
}
<div id="app">
<section class="set clearfix">
<div class="left">
<div class="head">
<img id="userPhoto" :src="uploadData.imgData">
</div>
<button id="showUpload" @click=showUpload>点击上传头像</button>
</div>
</section>
<div class="palbox" v-if="uploadData.uploadImgBox">
<div class="shadow-box"></div>
<div class="popup">
<h3 class="common-header">头像设置</h3>
<div class="content clearfix">
<div class="upload ceshi" v-show="!uploadData.anewImgBox">
<div class="button udlr">
<button>
<i class="iconfont icon-add"></i>
本地上传
<input class="upload-btn" @change="uploadPortrait" type="file" accept="image/gif, image/png, image/jpg, image/jpeg" >
</button>
<p class="hint">只支持JPG、PNG格式的图片,大小不超过5M</p>
</div>
</div>
<div class="opeaUpload" v-show="uploadData.anewImgBox">
<div class="opea-warp">
<div class="canvas-warp">
<canvas id="drawCanv" :class="{'drawCanv':true,'dragMouse':uploadData.dragMouse,'zoomMouse':uploadData.zoomMouse}" :style="uploadData.filmCanvas.styleObj" @mousedown ="downPortrait" @mousemove="movePortrait" @mouseup="upPortrait"></canvas>
</div>
<canvas id="filmCanv" class="filmCanv" :style="uploadData.filmCanvas.styleObj"></canvas>
</div>
<span class="anew-warp">
<input type="file" @change="uploadPortrait" accept="image/gif, image/png, image/jpg, image/jpeg" >
<button class="anew" type="button">重新上传</button>
</span>
</div>
<div class="view">
<div id="localImag">
<p class="yl">头像预览</p>
<div class="imgbox udlr">
<img id="preview" :src="uploadData.imgData">
</div>
</div>
</div>
</div>
<div class="operate">
<button class="cancel" @click="closeUpload">取消</button>
<button class="ensure" @click="saveUploadPortrait">确定</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
@charset "utf-8";
*{
margin:0;
padding:0;
}
.clearfix:after {
content: '\u0020';
display: block;
visibility: hidden;
height: 0;
clear: both;
}
button{
outline:none;
border:0;
cursor:pointer;
}
.set{
background: #FFFFFF;
box-shadow: 0 3px 12px 0 rgba(14, 34, 68, 0.06);
border-radius: 0 2px 2px 2px;
padding: 74px 0 134px 179px;
}
.left{
text-align: left;
}
.left .head{
width: 190px;
height: 190px;
border-radius: 50%;
background: #EDF8FF;
overflow: hidden;
}
.left .head img{
width: 100%;
height: 100%;
}
.left button{
width: 106px;
height: 30px;
margin-top: 32px;
background: #39B6E5;
box-shadow: 0 2px 6px 0 rgba(57, 182, 229, 0.26);
border-radius: 2px;
font-size: 12px;
color: #FFFFFF;
}
.palbox{
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
.shadow-box{
content:'';
display:block;
position:absolute;
left:0;
right:0;
bottom:0;
top:0;
background: rgba(0, 0, 0, 0.3);
}
.popup{
margin:35px auto;
position:relative;
width: 680px;
height: 570px;
background: #FFFFFF;
box-shadow: 0 3px 12px 0 rgba(14, 34, 68, 0.06);
border-radius: 2px;
z-index:999;
}
.popup .common-header{
padding-left: 20px;
height: 40px;
line-height: 40px;
font-size: 14px;
color: #28374F;
background: #F6F9FC;
border-radius: 5px 5px 0 0;
}
.content{
padding: 15px 30px;
}
.content .upload{
float: left;
position: relative;
margin-right: 16px;
width: 366px;
height: 430px;
border: 1px solid #E9EBED;
}
.content .button{
margin-top: 180px;
text-align: center;
}
.content .button button{
width: 106px;
height: 30px;
margin-bottom: 20px;
background: #39B6E5;
box-shadow: 0 2px 6px 0 rgba(57,182,229,0.26);
border-radius: 2px;
font-size: 12px;
color: #FFFFFF;
position: relative;
}
.content .upload-btn{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
.content .icon-add{
font-size: 12px;
color: #fff;
}
.content .opeaUpload{
padding: 18px 18px 0 18px;
width: 360px;
height: 410px;
border: 1px solid #E9EBED;
float: left;
margin-right: 16px;
position: relative;
}
.content .opea-warp{
position: relative;
width: 360px;
height: 360px;
background-color: #000;
}
.content .opea-warp .drawCanv{
position: relative;
z-index: 9999999;
}
.content .opea-warp .dragMouse{
cursor: move;
}
.content .opea-warp .zoomMouse{
cursor: nw-resize;
}
.content .opea-warp .filmCanv{
position: absolute;
top: 0;
left: 0;
z-index: 99;
}
.content .anew-warp{
position: relative;
margin: 10px 0 0 0;
display: inline-block;
width: 72px;
height: 26px;
cursor: pointer;
}
.content .anew-warp input[type=file]{
position: absolute;
left: 0;
top: 0;
opacity: 0;
z-index: 99;
cursor: pointer;
}
.content .anew{
width: 72px;
height: 26px;
line-height: 23px;
font-size: 12px;
color: #39B6E5;
background-color: #fff;
outline: none;
border: 1px solid #39B6E5;
border-radius: 3px;
text-align: center;
cursor: pointer;
}
.content .hint{
text-align: center;
font-size: 12px;
color: #A7B2BD;
}
.content .view{
width: 202px;
height: 430px;
float: left;
border: 1px solid #E9EBED;
position: relative;
}
.content .view .yl{
font-size: 12px;
color: #A7B2BD;
margin-top: 16px;
padding-left: 16px;
}
.content .view .imgbox{
margin:120px auto;
width: 120px;
height: 120px;
border-radius: 50%;
}
.content .view .imgbox img{
width: 100%;
border-radius: 50%;
}
.operate{
padding-right: 20px;
text-align: right;
}
.operate button{
width: 126px;
height: 36px;
color:#fff;
background-color:#39B6E5;
border-radius:3px;
}
.operate button:first-child{
margin-right: 8px;
color:#39B6E5;
background-color:#fff;
border:1px solid #39B6E5;
}