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

或者将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. ssm简单配置

    MyBatis 是一个可以自定义SQL.存储过程和高级映射的持久层框架. MyBatis 摒除了大部分的JDBC代码.手工设置参数和结果集重获. MyBatis 只使用简单的XML 和注解来配置和映射 ...

  2. c#文本转语音以及语音阅读小实例

    c#实现语音阅读以及文本转语音文件是基于c#的一个类库(SpeechSynthesizer )实现的,使用该类必须要添加引用using System.Speech.Synthesis;直接是无法添加引 ...

  3. ubuntu 16.04 Dockerfile 安装mysql

    默认上MariaDB的包并没有在Ubuntu仓库中.要安装MariaDB,我们首先要设置MariaDB仓库. 配置MariaDB 仓库 # apt-get install software-prope ...

  4. python 中使用 global 引发了莫名其妙的问题

    哪里出问题了 python 中,使用 global 会将全局变量设为本函数可用.同时,在函数内部访问变量会先本地再全局. 在嵌套函数中,使用 global 会产生不合常理的行为. 上代码: In [9 ...

  5. LINQ 的使用

    [转]链接:cnblogs.com/liqingwen/p/5832322.html LINQ 简介 语言集成查询 (LINQ) 是 Visual Studio 2008 和 .NET Framewo ...

  6. android之volley学习

    Volley是android的平台通信库,一个新的网络通信框架.Volley 的特点:适合数据量小,通信频繁的网络操作. 获取Volley git 工具使用:git clone https://and ...

  7. vim的配置与使用

    经历了一次source insight 一言不合就崩溃之后,决定还是花点时间好好配置和学习以下vim 于是找到大神的配置 https://github.com/humiaozuzu/dot-vimrc ...

  8. MySQL查询和删除重复数据

    删除表中重复记录,只保留一条: delete from 表名 where 字段ID in (select * from (select max(字段ID) from 表名 group by 重复的字段 ...

  9. NSIS 无边框移动问题总结笔记

    无边框移动 插件 WinProc WinCore.nsh [一定要有这个] 代码 ;事件 ;处理无边框移动 Function onGUICallback ${If} $MSG = ${WM_LBUTT ...

  10. Oracle EBS - Form DEV Env

    1. 创建文件夹resource与forms, 以便存放pll与forms(主要用到APSTAND.fmb, APPSTAND.fmb, TEMPLATE.fmb)文件; 2. 修改注册表 HKEY_ ...