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

或者将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. NFSv4的引用,迁移和备份(用户手册 v0.2)

    RFC3530 定义了NFS文件系统迁移和引用的管理机制.文件系统定位功能通过fs_location属性向客户端提供文件系统的位置信息.fs_location属 性是一个包含有位置信息的列表,位置信息 ...

  2. sublime Text 3 字体

    1,Comic Sans Ms 2,DejaVu Sans Mono 3,microsoft yahei(微软雅黑) 4,Microsoft Yahei UI(微软雅黑增强版) 5,Ubuntu Mo ...

  3. 视区相关单位vw, vh..简介以及可实际应用场景

    这篇文章发布于 2012年09月24日,星期一,01:15,归类于 css相关. 阅读 37012 次, 今日 12 次 by zhangxinxu from http://www.zhangxinx ...

  4. php获取excel所有的批注

    phpexcel下载:https://github.com/PHPOffice/PHPExcel <?php /** *获取excel所有的批注 * * 存在编码问题,xls和xlsx的批注编码 ...

  5. 安卓学习之--UI控件用法 单选 按钮 下拉框

    1.单选 .RadioGroup 可将各自不同的RadioButton ,设限于同一个Radio 按钮组,同一个RadioGroup 组里的按钮,只能做出单一选择(单选题). <RadioGro ...

  6. Web方式预览Office/Word/Excel/pdf文件解决方案

    最近在做项目时需要在Web端预览一些Office文件,经过在万能的互联网上一番搜索确定并解决了. 虽然其中碰到的一些问题已经通过搜索和自己研究解决了,但是觉得有必要将整个过程记录下来,以方便自己以后查 ...

  7. dom初识

    1什么是dom document object model文档对象模型 是将整个页面文档封装成了一个对象,就是一个文档对象 整个页面就是一个文档,是由很多的节点组成的节点又包括三部分: 元素 属性 文 ...

  8. MyBatis(增删改查)

    1.工程中引入包: mybatis-3.2.7.mysql-connector-java-5.1.22-bin 2.添加配置文件: <?xml version="1.0" e ...

  9. 走向面试之数据库基础:一、你必知必会的SQL语句练习-Part 1

    本文是在Cat Qi的参考原帖的基础之上经本人一题一题练习后编辑而成,非原创,仅润色而已.另外,本文所列题目的解法并非只有一种,本文只是给出比较普通的一种而已,也希望各位园友能够自由发挥. 一.三点一 ...

  10. 关于GC和析构函数的一个趣题

    这个有趣的问题感谢装配脑袋友情提供. 请看如下代码: public class Dummy { public static Dummy Instance; ; ~Dummy() { Instance ...