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

或者将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;

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

至此,移动端完美支持!

<!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

转载请注明。

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

  1. 转:【专题十一】实现一个基于FTP协议的程序——文件上传下载器

    引言: 在这个专题将为大家揭开下FTP这个协议的面纱,其实学习知识和生活中的例子都是很相通的,就拿这个专题来说,要了解FTP协议然后根据FTP协议实现一个文件下载器,就和和追MM是差不多的过程的,相信 ...

  2. 专题十一:实现一个基于FTP协议的程序——文件上传下载器

    引言: 在这个专题将为大家揭开下FTP这个协议的面纱,其实学习知识和生活中的例子都是很相通的,就拿这个专题来说,要了解FTP协议然后根据FTP协议实现一个文件下载器,就和和追MM是差不多的过程的,相信 ...

  3. 实现一个基于FTP协议的程序——文件上传下载器(十三)

    此为一个系列,后续会把内容补上...

  4. joomla安装插件报错:上传文件到服务器发生了一个错误。 过小的PHP文件上传尺寸

    在安装joomla的AKeeba插件的时候报错如下:上传文件到服务器发生了一个错误. 过小的PHP文件上传尺寸.解决方法是修改php.ini文件,打开文件后搜索upload_max_filesize! ...

  5. 每天一个linux命令(26)--用SecureCRT来上传和下载文件

    用SSH管理Linux 服务器时经常需要远程与本地之间交互文件,而直接使用 SecureCRT 自带的上传下载功能无疑是最方便的,SecureCRT下的文件传输协议有ASCII.Xmodem.Zmod ...

  6. java nio 写一个完整的http服务器 支持文件上传 chunk传输 gzip 压缩 使用过程 和servlet差不多

    java nio 写一个完整的http服务器  支持文件上传   chunk传输    gzip 压缩      也仿照着 netty处理了NIO的空轮询BUG        本项目并不复杂 代码不多 ...

  7. 一个项目中哪些文件是要上传到 git上的,哪些是不必要的

  8. JavaScript实现拖拽预览,AJAX小文件上传

    本地上传,提前预览(图片,视频) 1.html中div标签预览显示,button标签触发上传事件. <div  id="drop_area" style="bord ...

  9. 【JavaScript游戏开发】使用HTML5+Canvas+JavaScript 封装的一个超级马里奥游戏(包含源码)

    这个游戏基本上是建立在JavaScript模块化的开发基础上进行封装的,对游戏里面需要使用到的游戏场景进行了封装,分别实现了Game,Sprite,enemy,player, base,Animati ...

随机推荐

  1. linux(centos)下挂载ntfs文件系统

    在将硬盘插到Linux系统上,打开硬盘时一直提示:unknown filesystem type 'ntfs'.在尝试网上的方法也遇到了一些问题. 下面按照遇到的问题,按照正确的方式注意操作从而避免问 ...

  2. Nginx反向代理和负载均衡

    一.Nginx反向代理设置 从80端口转向其他端口反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的 ...

  3. 【Python】引用计数

    一.概述 要保持追踪内存中的对象,Python使用了引用计数这一简单的技术. 二.引用计数的增减 2.1 增加引用计数 当对象被创建并(将其引用)赋值给变量时,该对象的引用计数被设置为1. 对象的引用 ...

  4. vector、list、deque三者比较

    1.vector是一段连续的内存块,而deque是多个连续的内存块,list是所有数据元素分开保存,可以是任何两个元素都没有连续. 2.vector的查询性能最好,并且的末端增加数据也很好,除非它重新 ...

  5. navicat 结合快捷键

    ctrl+q 打开查询窗口ctrl+/ 注释sql语句ctrl+shift +/ 解除注释ctrl+r 运行查询窗口的sql语句ctrl+shift+r 只运行选中的sql语句F6 打开一个mysql ...

  6. JeeSite学习笔记~代码生成原理

    1.建立数据模型[单表,一对多表,树状结构表] 用ERMaster建立数据模型,并设定对应表,建立关联关系 2.系统获取对应表原理 1.怎样获取数据库的表 genTableForm.jsp: < ...

  7. Android 6.0 使用HttpURLConnection 使用Get提交遇到405等问题。

    HttpURLConnection 在调用connection.setDoOutput(true)之后会自动把提交方式改为POST.然后调用方法的时候有可能会出现这种情况 在调用get的时候设置为co ...

  8. SQL Server恢复软件SysTools SQL Recovery/SysTools SQL Server Recovery Manager

    SQL Server恢复软件SysTools SQL Recovery/SysTools SQL Server Recovery Manager http://www.systoolsgroup.co ...

  9. etcd:用于服务发现的键值存储系统

    etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现.etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理 ...

  10. 让Response.Redirect页面重定向更有效率

    用 Redirect 方法可将浏览器重定向到另一个 URL,而不是将内容发送给用户. 这里有一篇文章介绍使用Redirect<Using Response.Redirect Effectivel ...