用Canvas+Javascript FileAPI 实现一个跨平台的图片剪切、滤镜处理、上传下载工具

时间:2023-03-09 00:16:18
用Canvas+Javascript FileAPI 实现一个跨平台的图片剪切、滤镜处理、上传下载工具

直接上代码,其中上传功能需要自己配置允许跨域的文件服务器地址~

或者将html文件贴到您的站点下同源上传也OK。

支持:

不同尺寸图片获取、

原图缩小放大、

原图移动、

选择框大小改变、

下载选中的区域、

上传选中的区域、

几种简单的滤镜(自己添加滤镜函数即可添加滤镜效果)

移动端适配要点

① 替换事件名称

if(/^.*(Android|iPad|iPhone){1}.*$/.test(navigator.userAgent)){
eventName={down:"touchstart",move:"touchmove",up:"touchend",click:"tap"};
}

② 移动端touch事件e没有clientX属性,需要做如下处理

//处理事件,支持移动端
//e.originalEvent.targetTouches[0].pageX
function dealE(e){
e.clientX= e.clientX || e.originalEvent.targetTouches[0].clientX;
e.clientY= e.clientY || e.originalEvent.targetTouches[0].clientY;
}

③ 移动端浏览器展示网页在手指拖动的过程中是会左右晃悠的,体验十分不好。

给所有事件都加上

e.preventDefault();

④ 移动端浏览器对File上传支持不好,微信甚至干脆屏蔽了File上传请求

我的做法是:

获取图片文件的base64字符串:

 var imgData = $("#res1")[0].toDataURL("png");
imgData = imgData.replace(/^data:image\/(png|jpg);base64,/, "");

然后自己在后端实现一个文件上传代理,接收base64字符串,拼接body:

 proxyRequest.ContentType = "multipart/form-data; boundary=----WebKitFormBoundaryqwqoxnDz0J0XB2Ti";
StreamReader reader = new StreamReader(_context.Request.InputStream);
string base64=reader.ReadToEnd();
string divider = "----WebKitFormBoundaryqwqoxnDz0J0XB2Ti";
string content = "";
content += "--"+divider;
content += "\r\n";
content += "Content-Disposition: form-data; name=\"userlogo\"; filename=\"userlogo.png\"";
content += "\r\n";
content += "Content-Type: image/png";
content += "\r\n\r\n";
byte[] bytes1 = Encoding.UTF8.GetBytes(content);
byte[] bytes2 = Convert.FromBase64String(base64);
byte[] bytes3 = Encoding.UTF8.GetBytes("\r\n" + "--" + divider + "--\r\n");
byte[] bytes = new byte[bytes1.Length+bytes2.Length+bytes3.Length];
bytes1.CopyTo(bytes,);
bytes2.CopyTo(bytes, bytes1.Length);
bytes3.CopyTo(bytes, bytes1.Length + bytes2.Length);
proxyRequest.ContentLength = bytes.Length;

这样对于移动端浏览器来说这就是一个普通的请求。

至此,移动端完美支持!

用Canvas+Javascript FileAPI 实现一个跨平台的图片剪切、滤镜处理、上传下载工具

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>get image</title>
<style>
canvas{
border:solid thin #ccc;
cursor:pointer;
}
#canvasContainer{
position:relative;
}
#picker{
position:absolute;
border:solid thin #ccc;
cursor: move;
overflow:hidden;
z-index:2;
}
#resize{
width: 0;
height: 0;
border-bottom: 15px solid rgba(200,200,200,0.8);
border-left: 15px solid transparent;
right: 0;
bottom: 0;
position: absolute;
cursor: se-resize;
z-index:3;
}
</style>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script>
<script>
$(function(){
var canvas=document.getElementById("container"),
context=canvas.getContext("2d"),
//文件服务器地址
fileServer=null,
//适配环境,随时修改事件名称
eventName={down:"mousedown",move:"mousemove",up:"mouseup",click:"click"};
//////////canvas尺寸配置
var canvasConfig={
//容器canvas尺寸
width:500,
height:300,
//原图放大/缩小
zoom:1,
//图片对象
img:null,
//图片完整显示在canvas容器内的尺寸
size:null,
//图片绘制偏移,为了原图不移出框外,这个只能是负值or 0
offset:{x:0,y:0},
//当前应用的滤镜
filter:null
}
canvas.width=canvasConfig.width;
canvas.height=canvasConfig.height;
///////////设置选择工具配置
var config={
//图片选择框当前大小、最大大小、最小大小
pickerSize:100,
minSize:50,
maxSize:200,
x:canvas.width/2-100/2,
y:canvas.height/2-100/2
}
/////////////结果canvas配置
var resCanvas=[$("#res1")[0].getContext("2d"),$("#res2")[0].getContext("2d"),$("#res3")[0].getContext("2d")];
//结果canvas尺寸配置
var resSize=[100,50,32]
resSize.forEach(function(size,i){
$("#res"+(i+1))[0].width=size;
$("#res"+(i+1))[0].height=size;
});
//////// 滤镜配置
var filters=[];
filters.push({name:"灰度",func:function(pixelData){
//r、g、b、a
//灰度滤镜公式: gray=r*0.3+g*0.59+b*0.11
var gray;
for(var i=0;i<canvasConfig.width*canvasConfig.height;i++){
gray=pixelData[4*i+0]*0.3+pixelData[4*i+1]*0.59+pixelData[4*i+2]*0.11;
pixelData[4*i+0]=gray;
pixelData[4*i+1]=gray;
pixelData[4*i+2]=gray;
}
}});
filters.push({name:"黑白",func:function(pixelData){
//r、g、b、a
//黑白滤镜公式: 0 or 255
var gray;
for(var i=0;i<canvasConfig.width*canvasConfig.height;i++){
gray=pixelData[4*i+0]*0.3+pixelData[4*i+1]*0.59+pixelData[4*i+2]*0.11;
if(gray>255/2){
gray=255;
}
else{
gray=0;
}
pixelData[4*i+0]=gray;
pixelData[4*i+1]=gray;
pixelData[4*i+2]=gray;
}
}});
filters.push({name:"反色",func:function(pixelData){
for(var i=0;i<canvasConfig.width*canvasConfig.height;i++){
pixelData[i*4+0]=255-pixelData[i*4+0];
pixelData[i*4+1]=255-pixelData[i*4+1];
pixelData[i*4+2]=255-pixelData[i*4+2];
}
}});
filters.push({name:"无",func:null});
// 添加滤镜按钮
filters.forEach(function(filter){
var button=$("<button>"+filter.name+"</button>");
button.on(eventName.click,function(){
canvasConfig.filter=filter.func;
//重绘
draw(context,canvasConfig.img,canvasConfig.size);
})
$("#filters").append(button);
});
//下载生成的图片(只下载第一张)
$("#download").on(eventName.click,function(){ //将mime-type改为image/octet-stream,强制让浏览器直接download
var _fixType = function(type) {
type = type.toLowerCase().replace(/jpg/i, 'jpeg');
var r = type.match(/png|jpeg|bmp|gif/)[0];
return 'image/' + r;
};
var saveFile = function(data, filename){
var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
save_link.href = data;
save_link.download = filename;
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
save_link.dispatchEvent(event);
};
var imgData = $("#res1")[0].toDataURL("png");
imgData = imgData.replace(_fixType("png"),'image/octet-stream');//base64
saveFile(imgData,"头像created on"+new Date().getTime()+"."+"png");
});
//上传图片
$("#upload").on(eventName.click,function(){
var imgData = $("#res1")[0].toDataURL("png");
imgData = imgData.replace(/^data:image\/(png|jpg);base64,/, "");
if(!fileServer){
alert("请配置文件服务器地址");
return;
} var blobBin = atob(imgData);
var array = [];
for(var i = 0; i < blobBin.length; i++) {
array.push(blobBin.charCodeAt(i));
}
var blob=new Blob([new Uint8Array(array)], {type: 'image/png'});
var file=new File([blob],"userlogo.png", {type: 'image/png'});
var formdata=new FormData();
formdata.append("userlogo",file);
$.ajax({
type: 'POST',
url: fileServer,
data: formdata,
processData: false,
contentType: false,
success: function (msg) {
$("#uploadres").text(JSON.stringify(msg));
}
});
});
//绑定选择图片事件
$("#fileinput").change(function(){
var file=this.files[0],
URL = (window.webkitURL || window.URL),
url = URL.createObjectURL(file),
img=new Image();
img.src=url;
img.onload=function(){
canvasConfig.img=img;
canvasConfig.size=getFixedSize(img,canvas);
draw(context,img,canvasConfig.size);
setPicker();
} });
//移动选择框
//绑定鼠标在选择工具上按下的事件
$("#picker").on(eventName.down,function(e){
e.stopPropagation();
var start={x:e.clientX,y:e.clientY,initX:config.x,initY:config.y};
$("#canvasContainer").on(eventName.move,function(e){
// 将x、y限制在框内
config.x=Math.min(Math.max(start.initX+e.clientX-start.x,0),canvasConfig.width-config.pickerSize);
config.y=Math.min(Math.max(start.initY+e.clientY-start.y,0),canvasConfig.height-config.pickerSize);
setPicker();
})
});
//原图移动事件
$("#container").on(eventName.down,function(e){
e.stopPropagation();
var start={x:e.clientX,y:e.clientY,initX:canvasConfig.offset.x,initY:canvasConfig.offset.y};
var size=canvasConfig.size;
$("#canvasContainer").on(eventName.move,function(e){
// 将x、y限制在框内
// 坐标<0 当图片大于容器 坐标>容器-图片 否则不能移动
canvasConfig.offset.x=Math.max(Math.min(start.initX+e.clientX-start.x,0),Math.min(canvasConfig.width-size.width*canvasConfig.zoom,0));
canvasConfig.offset.y=Math.max(Math.min(start.initY+e.clientY-start.y,0),Math.min(canvasConfig.height-size.height*canvasConfig.zoom,0));
//重绘蒙版
draw(context,canvasConfig.img,canvasConfig.size);
})
});
//改变选择框大小事件
$("#resize").on(eventName.down,function(e){
e.stopPropagation();
var start={x:e.clientX,init:config.pickerSize};
$("#canvasContainer").on(eventName.move,function(e){
config.pickerSize= Math.min(Math.max(start.init+e.clientX-start.x,config.minSize),config.maxSize);
$("#picker").css({width:config.pickerSize,height:config.pickerSize});
draw(context,canvasConfig.img,canvasConfig.size);
})
});
$(document).on(eventName.up,function(e){
$("#canvasContainer").unbind(eventName.move);
})
//原图放大、缩小
$("#bigger").on(eventName.click,function(){
canvasConfig.zoom=Math.min(3,canvasConfig.zoom+0.1);
//重绘蒙版
draw(context,canvasConfig.img,canvasConfig.size);
})
$("#smaller").on(eventName.click,function(){
canvasConfig.zoom=Math.max(0.4,canvasConfig.zoom-0.1);
//重绘蒙版
draw(context,canvasConfig.img,canvasConfig.size);
}) // 定位选择工具
function setPicker(){
$("#picker").css({width:config.pickerSize+"px",height:config.pickerSize+"px",
top:config.y,left:config.x});
//重绘蒙版
draw(context,canvasConfig.img,canvasConfig.size);
}
//绘制canvas中的图片和蒙版
function draw(context,img,size){
var pickerSize=config.pickerSize,
zoom=canvasConfig.zoom,
offset=canvasConfig.offset;
context.clearRect(0,0,canvas.width,canvas.height);
context.drawImage(img,0,0,img.width,img.height,offset.x,offset.y,size.width*zoom,size.height*zoom);
//绘制挖洞后的蒙版
context.save();
context.beginPath();
pathRect(context,config.x,config.y,pickerSize,pickerSize);
context.rect(0,0,canvas.width,canvas.height);
context.closePath();
context.fillStyle="rgba(255,255,255,0.9)";
context.fill();
context.restore();
//绘制结果
var imageData=context.getImageData(config.x,config.y,pickerSize,pickerSize)
resCanvas.forEach(function(resContext,i){
resContext.clearRect(0,0,resSize[i],resSize[i]);
resContext.drawImage(canvas,config.x,config.y,pickerSize,pickerSize,0,0,resSize[i],resSize[i]);
//添加滤镜效果
if(canvasConfig.filter){
var imageData=resContext.getImageData(0,0,resSize[i],resSize[i]);
var temp=resContext.getImageData(0,0,resSize[i],resSize[i]);// 有的滤镜实现需要temp数据
canvasConfig.filter(imageData.data,temp);
resContext.putImageData(imageData,0,0,0,0,resSize[i],resSize[i]);
}
});
}
//逆时针用路径自己来绘制矩形,这样可以控制方向,以便挖洞
// 起点x,起点y,宽度,高度
function pathRect(context,x,y,width,height){
context.moveTo(x,y);
context.lineTo(x,y+height);
context.lineTo(x+width,y+height);
context.lineTo(x+width,y);
context.lineTo(x,y);
}
// 根据图片和canvas的尺寸,确定图片显示在canvas中的尺寸
function getFixedSize(img,canvas){
var cancasRate=canvas.width/canvas.height,
imgRate=img.width/img.height,width=img.width,height=img.height;
if(cancasRate>=imgRate && img.height>canvas.height){
height=canvas.height;
width=imgRate*height;
}
else if(cancasRate<imgRate && img.width>canvas.width){
width=canvas.width;
height=width/imgRate;
}
return {width:width,height,height}
}
});
</script>
</head>
<body>
<input id="fileinput" type="file" /><br/><br/>
<div id="canvasContainer">
<canvas id="container"></canvas>
<div id="picker">
<div id="resize"></div>
</div>
</div><br/>
<button id="bigger">原图放大</button><button id="smaller">原图缩小</button>
<p>结果:</p>
<div>
<canvas id="res1"></canvas>
<canvas id="res2"></canvas>
<canvas id="res3"></canvas>
<button id="download"> 下载 </button>
<button id="upload"> 上传 </button>(demo只上传/下载第一张图片)
<div id="uploadres"></div>
</div>
<p>滤镜:</p>
<div id="filters"></div>
</body>
</html>

原文地址:http://www.cnblogs.com/tzyy/p/4830439.html

转载请注明。