html5,js插件实现手机端头像剪切上传
思路:先打开相册,选取图片,在剪切图片,转化为base64格式,然后上传到七牛存储,返回url,再传给后端,整个流程就是这样。用的是angular框架,图像插件用到imagecropper.js,废话不多说,直接上代码。
效果:
用到的插件
<!DOCTYPE html> <html lang="en" ng-app=""> <head> <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no,width=device-width,height=device-height"/> <meta charset="UTF-8"> <title></title> <script type="text/javascript" src="app/lib/jquery.js"></script> <script type="text/javascript" src="app/lib/ionic/js/ionic.bundle.min.js"></script> <!-- 上传头像插件 --> <script src="app/lib/headimg/imagecropper.js"></script> <base href='/'/> </head> <body> <ion-nav-view> </ion-nav-view> <script type="text/javascript" src="app/dist/js/main-396e1d7cdb.js"></script> </body> </html>
页面
<ion-header-bar align-title="center" class="bar-light"> <button ui-sref="home.my" class="button button-clear"><i class="icon ion-ios-arrow-left"></i></button> <h1 class="title">个人资料</h1> </ion-header-bar> <ion-content class="my"> <ion-list> <ion-item class="item-input" style="height:1.1rem;" > <form id="form0" class="item-icon-right" href="javascript:void(0);" style="width: 100%"> <span class="input-label f26" style="margin-top:0.4rem; ">头像</span> <i class="icon "><img ng-src="{{info.headimgurl||'app/dist/img/my/me_img.png'}}" style="width: 0.8rem;position: absolute;top: 0.1rem;right:0.7rem;border-radius:50%;border:3px solid rgba(0,0,0,0.3)" /></i> <!-- <form name="form0" id="form0" style="width: 100%"> --> <input type="file" name="file0" id="file0" multiple="multiple"><br> <!-- </form> --> <i class="icon "><img src="app/dist/img/my/arrow_icon.png" class="arrow_icon"></i> </form> </ion-item> </ion-list> </ion-content> <div class="img-container"> <img id="img0" src=""> <div class="close"><button class="button button-large button-light">取消</button></div> <div class="saveBtn" on-tap="save()"><button class="button button-large button-light" >选取</button></div> </div> <img class="newImg" src="">
css
.albumsbox { width: 18.9375rem; position: absolute; bottom: 0; left: 0.53125rem; display: none; z-index: 999; } .albumsbox .albums, .albumsbox .cancel { width: 100%; height: 6.1875rem; background: #fff; border-radius: 0.625rem; color: #333333; font-size: 0.8125rem; line-height: 3.0625rem; text-align: center; } .albumsbox .albums .line, .albumsbox .cancel .line { border-bottom: 1px solid #d1d1d4; } .albumsbox .cancel { height: 3.0625rem; margin: 0.34375rem 0rem 0.5625rem 0rem; } .container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: none; background: #000; } .container #wrapper { width: 100%; height: 12.5rem; position: absolute; top: 40%; margin-top: -6.25rem; left: 0; } .container #wrapper #cropper { width: 100%; } .container #wrapper #previewContainer { display: none; } /*img prev*/ .img-container { width: 100%; height: 100%; position: absolute; top: 0; left: 0; display: none; background: #000; } .newImg { display: none; } .close { height: 1.1875rem; width: 30%; text-align: center; color: #fff; font-size: 0.8125rem; line-height: 1.1875rem; position: absolute; bottom: 1.5625rem; left: 0; } .saveBtn { height: 1.1875rem; width: 30%; text-align: center; color: #fff; font-size: 0.8125rem; line-height: 1.1875rem; position: absolute; bottom: 1.5625rem; right: 0; } .cropper-container { font-size: 0; line-height: 0; position: relative; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; direction: ltr !important; -ms-touch-action: none; touch-action: none; -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; } .cropper-container img { display: block; width: 100%; min-width: 0 !important; max-width: none !important; height: 100%; min-height: 0 !important; max-height: none !important; image-orientation: 0deg !important; } .cropper-wrap-box, .cropper-canvas, .cropper-drag-box, .cropper-crop-box, .cropper-modal { position: absolute; top: 0; right: 0; bottom: 0; left: 0; } .cropper-wrap-box { overflow: hidden; } .cropper-drag-box { opacity: 0; background-color: #fff; filter: alpha(opacity=0); } .cropper-modal { opacity: .5; background-color: #000; filter: alpha(opacity=50); } .cropper-view-box { display: block; overflow: hidden; width: 100%; height: 100%; outline: 1px solid #39f; outline-color: rgba(51, 153, 255, 0.75); } .cropper-dashed { position: absolute; display: block; opacity: .5; border: 0 dashed #eee; filter: alpha(opacity=50); } .cropper-dashed.dashed-h { top: 33.33333%; left: 0; width: 100%; height: 33.33333%; border-top-width: 1px; border-bottom-width: 1px; } .cropper-dashed.dashed-v { top: 0; left: 33.33333%; width: 33.33333%; height: 100%; border-right-width: 1px; border-left-width: 1px; } .cropper-center { position: absolute; top: 50%; left: 50%; display: block; width: 0; height: 0; opacity: .75; filter: alpha(opacity=75); } .cropper-center:before, .cropper-center:after { position: absolute; display: block; content: ' '; background-color: #eee; } .cropper-center:before { top: 0; left: -3px; width: 7px; height: 1px; } .cropper-center:after { top: -3px; left: 0; width: 1px; height: 7px; } .cropper-face, .cropper-line, .cropper-point { position: absolute; display: block; width: 100%; height: 100%; opacity: .1; filter: alpha(opacity=10); } .cropper-face { top: 0; left: 0; background-color: #fff; } .cropper-line { background-color: #39f; } .cropper-line.line-e { top: 0; right: -3px; width: 5px; cursor: e-resize; } .cropper-line.line-n { top: -3px; left: 0; height: 5px; cursor: n-resize; } .cropper-line.line-w { top: 0; left: -3px; width: 5px; cursor: w-resize; } .cropper-line.line-s { bottom: -3px; left: 0; height: 5px; cursor: s-resize; } .cropper-point { width: 5px; height: 5px; opacity: .75; background-color: #39f; filter: alpha(opacity=75); } .cropper-point.point-e { top: 50%; right: -3px; margin-top: -3px; cursor: e-resize; } .cropper-point.point-n { top: -3px; left: 50%; margin-left: -3px; cursor: n-resize; } .cropper-point.point-w { top: 50%; left: -3px; margin-top: -3px; cursor: w-resize; } .cropper-point.point-s { bottom: -3px; left: 50%; margin-left: -3px; cursor: s-resize; } .cropper-point.point-ne { top: -3px; right: -3px; cursor: ne-resize; } .cropper-point.point-nw { top: -3px; left: -3px; cursor: nw-resize; } .cropper-point.point-sw { bottom: -3px; left: -3px; cursor: sw-resize; } .cropper-point.point-se { right: -3px; bottom: -3px; width: 20px; height: 20px; cursor: se-resize; opacity: 1; filter: alpha(opacity=100); } .cropper-point.point-se:before { position: absolute; right: -50%; bottom: -50%; display: block; width: 200%; height: 200%; content: ' '; opacity: 0; background-color: #39f; filter: alpha(opacity=0); } @media (min-width: 768px) { .cropper-point.point-se { width: 15px; height: 15px; } } @media (min-width: 992px) { .cropper-point.point-se { width: 10px; height: 10px; } } @media (min-width: 1200px) { .cropper-point.point-se { width: 5px; height: 5px; opacity: .75; filter: alpha(opacity=75); } } .cropper-invisible { opacity: 0; filter: alpha(opacity=0); } .cropper-hide { position: absolute; display: block; width: 0; height: 0; } .cropper-hidden { display: none !important; } .cropper-move { cursor: move; }
js
/** * laoyou Module * * Description */ angular.module('app').controller('myEditCtrl', ['$scope', 'api', '$rootScope', '$http', '$state', '$stateParams','$ionicModal','qiniuConfig', function($scope, api, $rootScope, $http, $state, $stateParams,$ionicModal,qiniuConfig) { var showLoad = $rootScope.showLoad; $http.post("member/info",{test:1}).success(function(data){ $scope.info=data.data; if(!$scope.info.birthday){ $scope.info.birthday=new Date("1951-01-01"); }else{ $scope.info.birthday=new Date($scope.info.birthday); } }).error(function(){ $rootScope.loadFn.hide(); }); var config=angular.copy(qiniuConfig); // console.log(config) if(config){ var token=config.UpToken; } /*picBase是base64图片带头部的完整编码*/ function putb64(picBase){ /*picUrl用来存储返回来的url*/ var picUrl; /*把头部的data:image/png;base64,去掉。(注意:base64后面的逗号也去掉)*/ picBase=picBase.substring(22); /*通过base64编码字符流计算文件流大小函数*/ function fileSize(str) { var fileSize; if(str.indexOf('=')>0) { var indexOf=str.indexOf('='); str=str.substring(0,indexOf);//把末尾的’=‘号去掉 } fileSize=parseInt(str.length-(str.length/8)*2); return fileSize; } /*把字符串转换成json*/ function strToJson(str) { var json = eval('(' + str + ')'); return json; } var url = "http://up.qiniu.com/putb64/"+fileSize(picBase); var xhr = new XMLHttpRequest(); xhr.onreadystatechange=function() { if (xhr.readyState==4){ var keyText=xhr.responseText; // alert(keyText) /*返回的key是字符串,需要装换成json*/ keyText=strToJson(keyText); /* http://ojvh6i96g.bkt.clouddn.com/是我的七牛云空间网址,keyText.key 是返回的图片文件名*/ picUrl=config.recordPath+'/'+keyText.key; // alert(picUrl); $http.post("member/updateHead",{headimgurl:picUrl}).success(function (res) { if( res.statusCode == 0) { window.location.reload(); }else{ alert("修改失败"); } }).error(function(data){ alert("网络异常") }); } } xhr.open("POST", url, true); xhr.setRequestHeader("Content-Type", "application/octet-stream"); xhr.setRequestHeader("Authorization", "UpToken "+ token); //token换成自己申请七牛的。 xhr.send(picBase); } // var _czc = []; $("#file0").change(function(){ // _czc.push(["_trackEvent", "个人资料页","头像",""]); // console.log($("#img0")) var objUrl = getObjectURL(this.files[0]) ; console.log("objUrl = "+objUrl) ; if (objUrl) { $("#img0").attr("src", objUrl) ; $('.cropper-canvas img').attr('src',objUrl); $('.cropper-view-box img').attr('src',objUrl); } var File=$('#img0').attr('src'); if(File!=''||File==undefined){ // $scope.openModal(); $(".my").css('opacity','0'); $('.img-container').show(); } }) ; //建立一個可存取到該file的url function getObjectURL(file) { console.log(file) var url = null ; if (window.createObjectURL!=undefined) { // basic url = window.createObjectURL(file) ; } else if (window.URL!=undefined) { // mozilla(firefox) url = window.URL.createObjectURL(file) ; } else if (window.webkitURL!=undefined) { // webkit or chrome url = window.webkitURL.createObjectURL(file) ; } return url ; } $('.close').click(function(){ $(".my").css('opacity','1'); $('.img-container').hide(); }) function convertToData(url, canvasdata, cropdata, callback) { var cropw = cropdata.width; // 剪切的宽 var croph = cropdata.height; // 剪切的宽 var imgw = canvasdata.width; // 图片缩放或则放大后的高 var imgh = canvasdata.height; // 图片缩放或则放大后的高 var poleft = canvasdata.left - cropdata.left; // canvas定位图片的左边位置 var potop = canvasdata.top - cropdata.top; // canvas定位图片的上边位置 var canvas = document.createElement("canvas"); var ctx = canvas.getContext('2d'); canvas.width = cropw; canvas.height = croph; var img = new Image(); img.src = url; img.onload = function() { this.width = imgw; this.height = imgh; // 这里主要是懂得canvas与图片的裁剪之间的关系位置 ctx.drawImage(this, poleft, potop, this.width, this.height); var base64 = canvas.toDataURL('image/jpg', 1); // 这里的“1”是指的是处理图片的清晰度(0-1)之间,当然越小图片越模糊,处理后的图片大小也就越小 callback && callback(base64) // 回调base64字符串 } } $(function(){ var $image = $('.img-container > img'); $image.on("load", function() { // 等待图片加载成功后,才进行图片的裁剪功能 $image.cropper({ aspectRatio: 1 / 1 // 1:1的比例进行裁剪,可以是任意比例,自己调整 }); }) // 点击保存 // $(".saveBtn").on("click", function() { $scope.save = function(){ var src = $image.eq(0).attr("src"); var canvasdata = $image.cropper("getCanvasData"); var cropBoxData = $image.cropper('getCropBoxData'); convertToData(src, canvasdata, cropBoxData, function(basechar) { // 回调后的函数处理 $(".newImg").attr("src", basechar); // alert(basechar) putb64(basechar); }); } }) }]);
其中qiniuConfig是自己向后台要的,这是上传图片的身份凭证,这是我的qiniuConfig。
这是我头像上传页面的路由。
如有什么问题,欢迎指正。