此文是半原创。
头像裁剪的主要实现是同事找来发我的,用着还不错。
记一下,可以用作以后研究。
此文主要记录一个要点:
当用户上传已上传头像,裁剪头像弹窗获取到图片,当调用canvas的toDataURL方法时可能会报:Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported这个错误。
百度上主要方法有2步:
一是给图片添加crossOrigin(具体添加在下方依赖js里有,此处只是简单说明)
obj.image.crossOrigin = 'anonymous';
二是给请求服务器配置相应的跨域(自行百度搜索吧,服务器我接触不到)
"Access-Control-Allow-Origin: *"
当然,这是网上最多的两种方式。如果还是不行(我的问题是即使经过了上面两个步骤,浏览器仍然提示跨域。),那就试试给请求图片加个时间戳吧。
obj.image.src = options.imgSrc + "?" + new Date().getTime()
我的难题到这一步已经解决了,至于为什么加时间戳可行,目前还不是很清楚。如果大佬愿意解此疑惑,欢迎评论。
。。。。。。。。。。。。。我又来了,自己照旧百度了一下这个问题。
发现有时候我们会在请求静态资源的时候添加时间戳,防止浏览器缓存。
于是我去掉时间戳,在谷歌浏览器控制台------network里面勾选disable cache 后,再次访问,成功获取图片。
也就是说浏览器先前已经缓存了我的图片地址,然而此时我又一次请求了它,并且请求的时候我设置了crossOrigin属性,可是缓存中的图片非CORS请求,所以浏览器直接就拒绝了请求
效果图:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>头像裁剪</title>
<link rel="stylesheet" type="text/css" href="下面css" />
</head>
<body>
<div class="container">
<div class="close" onclick="window.parent.topLayerService.hideTopLayer();">关闭</div>
<div class="box">
<div class="imageBox" id="head_portrait">
<div class="thumbBox"></div>
<div class="spinner" style="display: none">Loading...</div>
</div>
<div class="action">
<div class="new-contentarea tc">
<a href="javascript:void(0)" class="upload-img">
<label for="upload-file">上传图像</label>
</a>
<input type="file" class="" name="upload-file" id="upload-file" />
</div>
<input type="button" id="btnZoomIn" class="Btnsty_peyton" value="+" />
<input type="button" id="btnZoomOut" class="Btnsty_peyton" value="-" />
<input type="button" id="btnCrop" class="Btnsty_peyton" value="裁切" />
</div>
</div>
<div class="cropped">
<h2>效果预览</h2>
<div class="croppedImg"></div>
<div class="exhibition" id="exhibition"></div>
<input type="button" id="submit" class="Btnsty_peyton" value="确认修改" />
</div>
</div>
<script type="text/javascript" src="../../../lib/jquery.js"></script>
<script type="text/javascript" src="下面js"></script>
<script type="text/javascript">
$(window).ready(function () {
var options =
{
thumbBox: '.thumbBox',
spinner: '.spinner',
imgSrc: ''
}
var cropper = $('.imageBox').cropbox(options);
init();
function init() {
//初始获取用户头像图片,成功回调
callback: function (data) {//渲染内容
var nowdata = data[0];
if (nowdata.head_portrait != undefined) {
options.imgSrc = "XXXXXXXXXX.jpg";
// 读取图片是否添加时间戳
options.initFlag = true;
cropper = $('.imageBox').cropbox(options);
}
}
}
// 切换图片
$('#upload-file').on('change', function () {
var reader = new FileReader();
// file 将文件读取为 DataURL
reader.readAsDataURL(this.files[0]);
reader.onload = function (e) {
options.imgSrc = e.target.result;
// 读取图片是否添加时间戳
options.initFlag = false;
cropper = $('.imageBox').cropbox(options);
}
reader.onerror = function (e) {
alert('图片上传失败,请检查。');
}
this.files = [];
})
// 裁切图片
$('#btnCrop').on('click', function () {
var img = cropper.getDataURL();
$('.croppedImg').html('');
$('.croppedImg').append('<div class="headerUP"><img src="' + img + '" align="absmiddle"></div><div class="headerSize">66px*66px</div>');
// $('.cropped').append('<img src="' + img + '" align="absmiddle" style="width:128px;margin-top:4px;border-radius:128px;box-shadow:0px 0px 12px #7E7E7E;"><p>128px*128px</p>');
// $('.cropped').append('<img src="' + img + '" align="absmiddle" style="width:180px;margin-top:4px;border-radius:180px;box-shadow:0px 0px 12px #7E7E7E;"><p>180px*180px</p>');
})
// 放大图片
$('#btnZoomIn').on('click', function () {
cropper.zoomIn();
})
// 缩小图片
$('#btnZoomOut').on('click', function () {
cropper.zoomOut();
})
// 提交
$('#submit').on('click', function () {
var files = new File([cropper.getBlob()], '.jpg', { type: 'image/jpg' });
upload(files);
});
// 上传
function upload(file) {
var formData = new FormData();
formData.append("file", file);
formData.append("serviceToken", getCookie("serviceToken"));
formData.append("time", getCookie("time"));
formData.append("token", getCookie("serverToken"));
formData.append("userid", getCookie("userid"));
formData.append("serverToken", getCookie("serverToken"));
$.ajax({
url:"XXXXXXXXXXXXX",//上传服务
type: "post",
data: formData,
async: false,
cache: false,
contentType: false,
processData: false,
success: function (data) {
//发送获取用户信息成功后的回调
callback: function (data1) {//渲染内容
alert(data1 == "success" ? "更新头像成功" : "更新头像失败!"
$("#exhibition").html("<img src='" + imageServerUrl + data.substr(12) + "'/>");
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.info(XMLHttpRequest.status);
console.info(XMLHttpRequest.readyState);
console.info(textStatus);
alert("文件上传失败!")
},
complete: function (XMLHttpRequest, textStatus) {
this; // 调用本次AJAX请求时传递的options参数
}
});
return false;
}
});
</script>
</body>
</html>
/** 依赖js文件
* Created by ezgoing on 14/9/2014.
*/
"use strict";
(function (factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(jQuery);
}
}(function ($) {
var cropbox = function (options, el) {
var el = el || $(options.imageBox),
obj =
{
state: {},
ratio: 1,
options: options,
imageBox: el,
thumbBox: el.find(options.thumbBox),
spinner: el.find(options.spinner),
image: new Image(),
getDataURL: function () {
var width = this.thumbBox.width(),
height = this.thumbBox.height(),
canvas = document.createElement("canvas"),
dim = el.css('background-position').split(' '),
size = el.css('background-size').split(' '),
dx = parseInt(dim[0]) - el.width() / 2 + width / 2,
dy = parseInt(dim[1]) - el.height() / 2 + height / 2,
dw = parseInt(size[0]),
dh = parseInt(size[1]),
sh = parseInt(this.image.height),
sw = parseInt(this.image.width);
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d");
context.drawImage(this.image, 0, 0, sw, sh, dx, dy, dw, dh);
//toDataURL将canvas对象转换为base64位编码
var imageData = canvas.toDataURL('image/png');
return imageData;
},
// 将base64转成图片文件
getBlob: function () {
var imageData = this.getDataURL();
var b64 = imageData.replace('data:image/png;base64,', '');
var binary = atob(b64);
var array = [];
for (var i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], { type: 'image/png' });
},
// 图片放大
zoomIn: function () {
this.ratio *= 1.1;
setBackground();
},
// 图片缩小
zoomOut: function () {
this.ratio *= 0.9;
setBackground();
}
},
// 设置图片大小
setBackground = function () {
var w = parseInt(obj.image.width) * obj.ratio;
var h = parseInt(obj.image.height) * obj.ratio;
var pw = (el.width() - w) / 2;
var ph = (el.height() - h) / 2;
el.css({
'background-image': 'url(' + obj.image.src + ')',
'background-size': w + 'px ' + h + 'px',
'background-position': pw + 'px ' + ph + 'px',
'background-repeat': 'no-repeat'
});
},
imgMouseDown = function (e) {
e.stopImmediatePropagation();
obj.state.dragable = true;
obj.state.mouseX = e.clientX;
obj.state.mouseY = e.clientY;
},
imgMouseMove = function (e) {
e.stopImmediatePropagation();
if (obj.state.dragable) {
var x = e.clientX - obj.state.mouseX;
var y = e.clientY - obj.state.mouseY;
var bg = el.css('background-position').split(' ');
var bgX = x + parseInt(bg[0]);
var bgY = y + parseInt(bg[1]);
el.css('background-position', bgX + 'px ' + bgY + 'px');
obj.state.mouseX = e.clientX;
obj.state.mouseY = e.clientY;
}
},
imgMouseUp = function (e) {
e.stopImmediatePropagation();
obj.state.dragable = false;
},
zoomImage = function (e) {
e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0 ? obj.ratio *= 1.1 : obj.ratio *= 0.9;
setBackground();
}
obj.spinner.show();
obj.image.onload = function () {
obj.spinner.hide();
setBackground();
el.bind('mousedown', imgMouseDown);
el.bind('mousemove', imgMouseMove);
$(window).bind('mouseup', imgMouseUp);
el.bind('mousewheel DOMMouseScroll', zoomImage);
};
obj.options.initFlag ? obj.image.src = options.imgSrc + "?" + new Date().getTime() : obj.image.src = options.imgSrc;
obj.image.crossOrigin = 'anonymous';
el.on('remove', function () { $(window).unbind('mouseup', imgMouseUp) });
return obj;
};
jQuery.fn.cropbox = function (options) {
return new cropbox(options, this);
};
}));
/*
* 依赖css文件
*/
.container {
width: 660px;
margin: 40px auto 0 auto;
position: relative;
color: #ffffff;
font-size: 14px;
box-shadow: -14px 0px 36px 7px rgba(108, 108, 108, 0.08);
border-radius: 6px;
overflow: hidden;
}
.close{
width: 20px;
height: 20px;
background: url(../../../image/imageUp/guanbida1.png) no-repeat 0/100%;
position: absolute;
right: 8px;
top: 8px;
z-index: 3;
font-size: 0;
cursor: pointer;
}
.container p {
line-height: 12px;
line-height: 0px;
height: 0px;
margin: 10px;
color: #bbb;
}
.action {
width: 340px;
height: 36px;
position: absolute;
bottom: 28px;
left: 30px;
}
.cropped {
width: 270px;
height: 502px;
position: absolute;
right: 0px;
top: 0;
box-shadow: 0px 0px 12px #ddd;
text-align: center;
background: #ffffff;
}
.cropped h2 {
margin: 53px 0 34px;
font-size: 16px;
font-weight: 500;
color: #333333;
line-height: 16px;
}
.box {
width: 390px;
height: 502px;
position: relative;
overflow: hidden;
background: rgba(0, 0, 0, 0.7);
}
.imageBox {
width: 276px;
height: 356px;
margin: 44px auto 0;
position: relative;
/* overflow: hidden; */
background-repeat: no-repeat;
cursor: move;
}
.imageBox .thumbBox {
width: 200px;
height: 200px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -100px;
margin-left: -100px;
box-sizing: border-box;
border: 1px solid rgb(102, 102, 102);
}
.imageBox .spinner {
text-align: center;
line-height: 356px;
background: rgba(255, 255, 255);
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
color: #333333;
}
.Btnsty_peyton {
display: inline-block;
*display: inline;
*zoom: 1;
vertical-align: top;
width: 50px;
height: 36px;
padding: 0;
margin-left: 4px;
background: linear-gradient(0deg, #3377ff, #57a5ff);
border-radius: 4px;
outline: none;
border: none;
color: #ffffff;
cursor: pointer;
}
#btnZoomIn {
margin-left: 55px;
background: url(../../../image/imageUp/zoomIn.png) no-repeat 0/100%;
font-size: 0;
}
#btnZoomOut{
background: url(../../../image/imageUp/zoomOut.png) no-repeat 0/100%;
font-size: 0;
}
#btnCrop {
width: 56px;
}
.croppedImg {
height: 85px;
text-align: center;
}
.headerUP {
width: 66px;
height: 66px;
margin: 0 auto;
border-radius: 100%;
box-shadow: 0px 0px 12px #7e7e7e;
overflow: hidden;
}
.headerUP img {
width: 66px;
height: 66px;
}
.exhibition {
width: 246px;
height: 180px;
margin: 9px auto 67px;
}
.exhibition img{
width: 246px;
height: 180px;
}
.cropped .Btnsty_peyton {
margin: 0 auto;
width: 110px;
height: 36px;
}
/*选择文件上传*/
.new-contentarea {
width: 110px;
overflow: hidden;
margin: 0 auto;
position: relative;
float: left;
}
.new-contentarea label {
width: 100%;
height: 100%;
display: block;
}
.new-contentarea input[type="file"] {
width: 110px;
height: 36px;
background: #333;
margin: 0 auto;
position: absolute;
top: 0;
left: 0;
opacity: 0;
filter: alpha(opacity=0);
z-index: 2;
}
a.upload-img {
display: inline-block;
*display: inline;
*zoom: 1;
width: 110px;
height: 36px;
line-height: 36px;
background: linear-gradient(0deg, #3377ff, #57a5ff);
border-radius: 4px;
text-decoration: none;
color: #ffffff;
cursor: pointer;
}
a.upload-img:hover {
background-color: #ec7e70;
}
.tc {
text-align: center;
}