需求:用户上传或者修改头像的时候先将图片裁剪,固定宽度和高度,这样在不同的情景下显示的时候不会出现压缩失真。
中间折腾过的方法:
1.smartCorp.js
国外一个大神写的,根据图像的饱和度裁剪最好看的照片,刚开始以为很好用的,在官网上下载了源码,demo跑起来还可以,但是裁剪成小图片的时候裁剪不了,项目是Java的,但是demo用的是PHP,我也不会PHP,也没有运行环境,最后放弃了。
2.JS素材网上下载的插件。也是一样的问题,裁剪成小图片的后台代码运行不了,而且样式也不好看,还是选择放弃。
3.JCorp.js 这也是国外的开源软件,第一眼看上去清新大方,有点意思。还有详尽的使用方法,不错不错。但是最核心的裁剪问题依然存在。有博客上用的是后台的Java代码裁剪,demo支持直接本地读取,和写入,没有问题,但是放在网页上,图片走服务器上传是很麻烦的一个问题,除了上传原图,还要返回裁剪后的图片base64码,这样子显示是没问题了 ,但是怎么上传到阿里云服务器又是问题了。找啊找,终于找到一个博客写的是用前端H5的<canvas>画布裁剪,不走服务器,很nice。于是乎直接放在项目里用了,集成好也没问题,很开心。开心了一分钟,然后,流文件怎么上传到阿里云服务器呢,翻遍了阿里云的社区和手册也没找到答案。然后简书上的一篇文章讲清楚了要怎么处理上传,搬过来,解决好了。最后还有一个问题,如何上传到网易云呢?翻了网易云的开发文档,用断点走了几遍js文件,找到了上传的文件格式,一遍一遍的试,最后也搞好了。超级超级开心的。以下是实现代码:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="description" id="description1" content="1">
<meta name="renderer" content="webkit">
<meta name="format-detection" content="telephone=no">
<title>图片上传</title>
<style type="text/css">
.jcrop-holder {
direction: ltr;
text-align: center;
margin: 0 auto;
/* IE10 touch compatibility */
-ms-touch-action: none;
}
/* Selection Border */
.jcrop-vline,
.jcrop-hline {
background: #ffffff url("Jcrop.gif");
font-size: 0;
position: absolute;
}
.jcrop-vline {
height: 100%;
width: 1px !important;
}
.jcrop-vline.right {
right: 0;
}
.jcrop-hline {
height: 1px !important;
width: 100%;
}
.jcrop-hline.bottom {
bottom: 0;
}
/* Invisible click targets */
.jcrop-tracker {
height: 100%;
width: 100%;
/* "turn off" link highlight */
-webkit-tap-highlight-color: transparent;
/* disable callout, image save panel */
-webkit-touch-callout: none;
/* disable cut copy paste */
-webkit-user-select: none;
}
/* Selection Handles */
.jcrop-handle {
background-color: #333333;
border: 1px #eeeeee solid;
width: 7px;
height: 7px;
font-size: 1px;
}
.jcrop-handle.ord-n {
left: 50%;
margin-left: -4px;
margin-top: -4px;
top: 0;
}
.jcrop-handle.ord-s {
bottom: 0;
left: 50%;
margin-bottom: -4px;
margin-left: -4px;
}
.jcrop-handle.ord-e {
margin-right: -4px;
margin-top: -4px;
right: 0;
top: 50%;
}
.jcrop-handle.ord-w {
left: 0;
margin-left: -4px;
margin-top: -4px;
top: 50%;
}
.jcrop-handle.ord-nw {
left: 0;
margin-left: -4px;
margin-top: -4px;
top: 0;
}
.jcrop-handle.ord-ne {
margin-right: -4px;
margin-top: -4px;
right: 0;
top: 0;
}
.jcrop-handle.ord-se {
bottom: 0;
margin-bottom: -4px;
margin-right: -4px;
right: 0;
}
.jcrop-handle.ord-sw {
bottom: 0;
left: 0;
margin-bottom: -4px;
margin-left: -4px;
}
/* Dragbars */
.jcrop-dragbar.ord-n,
.jcrop-dragbar.ord-s {
height: 7px;
width: 100%;
}
.jcrop-dragbar.ord-e,
.jcrop-dragbar.ord-w {
height: 100%;
width: 7px;
}
.jcrop-dragbar.ord-n {
margin-top: -4px;
}
.jcrop-dragbar.ord-s {
bottom: 0;
margin-bottom: -4px;
}
.jcrop-dragbar.ord-e {
margin-right: -4px;
right: 0;
}
.jcrop-dragbar.ord-w {
margin-left: -4px;
}
/* The "jcrop-light" class/extension */
.jcrop-light .jcrop-vline,
.jcrop-light .jcrop-hline {
background: #ffffff;
filter: alpha(opacity=70) !important;
opacity: .70!important;
}
.jcrop-light .jcrop-handle {
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
background-color: #000000;
border-color: #ffffff;
border-radius: 3px;
}
/* The "jcrop-dark" class/extension */
.jcrop-dark .jcrop-vline,
.jcrop-dark .jcrop-hline {
background: #000000;
filter: alpha(opacity=70) !important;
opacity: 0.7 !important;
}
.jcrop-dark .jcrop-handle {
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
background-color: #ffffff;
border-color: #000000;
border-radius: 3px;
}
/* Simple macro to turn off the antlines */
.solid-line .jcrop-vline,
.solid-line .jcrop-hline {
background: #ffffff;
}
/* Fix for twitter bootstrap et al. */
.jcrop-holder img,
img.jcrop-preview {
max-width: none;
}
.uploadPics {
position: relative;
width: 380px;
background-color: #fff;
height: 460px;
overflow: hidden;
}
.uploadPics > img {
position: absolute;
top: 20px;
right: 10px;
cursor: pointer;
}
.uploadPics .picTil {
padding: 20px;
font-size: 16px;
color: #323232;
border-bottom: 1px solid #f3f3f3;
}
.uploadPics .picCont {
margin: 20px;
padding: 15px;
width: 300px;
height: 337px;
background-color: #f2f2f5;
}
.uploadPics .picCont > p {
margin-top: 20px;
text-align: center;
}
.uploadPics .picFooter {
text-align: center;
}
.uploadPics .picFooter{
display: inline-block;
margin: 20px;
width: 130px;
height: 35px;
font-size: 18px;
line-height: 35px;
color: #fff;
border-radius: 5px;
cursor: pointer;
}
.uploadPics .picFooter .upload {
background-color: #aaa;
}
.uploadPics .picFooter .confirm {
background-color: #ed2828;
}
#myCan{
position: absolute;
top: 86px;
right: 110px;
}
.info{
position: absolute;
top: 16px;
right: 196px;
}
</style>
<link rel="stylesheet" href="<%=request.getContextPath()%>/Jcrop/css/jquery.Jcrop.css" type="text/css" />
<script type="text/javascript" src="<%=request.getContextPath()%>/Jcrop/js/jquery.Jcrop.js"></script>
</head>
<body>
<div id="showbg" style="display: none;">
<div class="uploadPics">
<div class="picCont" style="width:300px;height:300px;margin:20px auto 0;padding:0;" >
<div id=imgfield style=overflow:hidden;width:100%;height:100% ></div>
</div>
<div class="picFooter">
<input type="file" accept=".jpg,.jpeg,.png,.gif" id="fileimg" name="fileimg" style="display:none" onchange="imgchange()" />
<span class="layui-btn layui-btn-sm" onclick="getimg()">上传原图</span>
<!-- <span class="btn confirm" onclick="subform()">确认</span> -->
</div>
</div>
<div class="info">裁剪后图片:<br>图片大小:200 × 200</div>
<canvas id="myCan" width="200" height="200"></canvas>
</div>
</body>
<script type="text/javascript">
//上传的文件名
var fileName ="";
// var nim = '';
function subform() {
if($("#imgfield").html()){
var ossurl = "";
//获取裁剪完后的base64图片url,转换为blob
var urlData=document.getElementById("myCan").toDataURL();
var blob = dataURLtoBlob(urlData);
// blob转arrayBuffer
var reader = new FileReader();
reader.readAsArrayBuffer(blob);
reader.onload = function (event) {
var opts = {
url : "web?module=stwmgr&action=Advertisement&method=getOSSSecurityToken&tokenId=<%=request.getParameter("tokenId")%>",
type : "POST",
processData : false,
contentType : false,
dataType : "json",
async:false,
success : function(token) {
var client = new OSS.Wrapper({
accessKeyId : token.accessKeyId,//key
accessKeySecret : token.accessKeySecret,//密码、
stsToken : token.securityToken,
region : token.ossRegion,//阿里云服务器地址
bucket : token.ossBucket,//上传的到的文件夹
secure:true
});
var key = "stw_mgr/images/portrait_images/" + getCurrentDate() + getExtension(fileName);
//arrayBuffer转buffer
var buffer = new OSS.Buffer(event.target.result);
//上传接口改为put
client.put(key, buffer).then(function(result) {
console.log(result);
var url = client.signatureUrl(key);
$("input[name='portraitUri']").val(key);
$("#portraitImage").attr("src", url);
console.log(url);
ossurl = url;
}).catch(function(err) {
console.log(err.message);
});
}
};
$.ajax(opts);
}
// uploadTitleImage(blob); //网易云信上传
nim.sendFile({
scene: 'p2p',
to: 'account',
type: 'image',
blob: blob,
// wxFilePath: neteaseFile,
beginupload: function(upload) {
// - 如果开发者传入 fileInput, 在此回调之前不能修改 fileInput
// - 在此回调之后可以取消图片上传, 此回调会接收一个参数 `upload`, 调用 `upload.abort();` 来取消文件上传
},
uploadprogress: function(obj) {
console.log('文件总大小: ' + obj.total + 'bytes');
console.log('已经上传的大小: ' + obj.loaded + 'bytes');
console.log('上传进度: ' + obj.percentage);
console.log('上传进度文本: ' + obj.percentageText);
},
uploaddone: function(error, file) {
console.log(file);
$('#neteaseUri').val(file.url);
console.log('上传' + (!error?'成功':'失败'));
}
});
}
}
function getimg() {
$("#fileimg").click();
}
function imgchange() {
var localimg = $("#fileimg").get(0).files[0];
if(!localimg){
return;
}
fileName = localimg.name;
var fileSize = localimg.size;
var fileType=fileName.substring(fileName.lastIndexOf('.'),fileName.length).toLowerCase();
if(fileType!='.gif' && fileType!='.jpeg' && fileType!='.png' && fileType!='.jpg')
{
alert("上传失败,请上传jpg,jpeg,png格式的图片");
return;
}
var size=10*1024*1024;
if(fileSize>size){
alert("上传失败,请上传10MB以内的图片。");
return;
}
var reader=new FileReader();
//将文件读取为DataURL
reader.readAsDataURL(localimg);
reader.onload= function (e) {
var localimghtml = '<img id="cropbox" src="' + e.target.result + '" >';
$("#imgfield").html(localimghtml);
initJcrop();
};
}
function initJcrop(){
$('#cropbox').Jcrop({
onSelect: updateCoords,
aspectRatio: 1,
boxWidth: 300,
boxHeight: 300
}, function () {
//图片实际尺寸
var bb = this.getBounds();
var bWidth= Number(bb[0]) / 2;
var bHeight= Number(bb[1]) / 2;
this.setSelect([0, 0, bWidth,bHeight]);
var ss = this.getWidgetSize();
var aheight = (300 - Number(ss[1])) / 2 + "px";
$(".jcrop-holder").css("margin-top", aheight);
});
}
function updateCoords(c){
// console.log(c);
var img=document.getElementById("cropbox");
var ctx=document.getElementById("myCan").getContext("2d");
//img,开始剪切的x,Y坐标宽高,放置图像的x,y坐标宽高。
ctx.drawImage(img,c.x,c.y, c.w, c.h,0,0,200,200);
}
//**dataURL to blob**
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
</script>
</html>
//点击头像框触发事件
$("#portraitImage").click(function(){
var c=document.getElementById("myCan");
var cxt=c.getContext("2d");
cxt.clearRect(0,0,c.width,c.height);
var porsrc = this.src;
var orginimg = '<img id="cropbox" src="" crossorigin="Anonymous" >'; //" crossorigin="Anonymous"
//var image = new Image();
//image.setAttribute("crossOrigin",'Anonymous')
//image.crossOrigin = "Anonymous";
//image.src = porsrc;
$("#imgfield").html(orginimg); //把原有的头像图片添加到可编辑框里
$("#cropbox").attr("src",porsrc);
//初始化插件方法
initJcrop();
layer.open({
type: 1,
scrollbar: false,
title: ['头像裁剪框', 'font-size:16px;'],
area: ['800px', '500px'],
btn: ['保存'],
tipsMore: true,
content: $("#showbg"),
yes: function(index, layero){
subform();
$('#showbg').css('display','none');
layer.closeAll();
},
cancel: function(){
$('#showbg').css('display','none');
}
});
});
最后想了一个改进的地方,就是之前已经有头像的用户,可能会复用以前的头像,所以点击修改的时候要把原来的图片带到图片处理框里。这里遇到了图片的跨越请求问题。解决办法是在img标签添加一个属性:crossorigin="Anonymous"。
前后大概5天的时间做了这么一个小小的功能,收获了图像处理的知识,虽然理解上并没有很深,但是能运用起来,也蛮有成就感的了。记录下探索的历程。望更进一步。
JCrop官网地址:http://deepliquid.com/content/Jcrop_Download.html