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

或者将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. Nonblocking I/O and select()

    This sample program illustrates a server application that uses nonblocking and the select() API. Soc ...

  2. 来自XP的道别信

    当你们看到这封信的时候,很抱歉要和大家说再见了! 亲爱的你们,对不起,请不要为我哭泣,请让我们一起度过这最后的时光. 请记住那个在蓝天白云下奔跑的我!

  3. elasticsearch常用的概念整理

    节点node 节点(node)是一个运行着的Elasticsearch实例 集群中一个节点会被选举为主节点(master),它将临时管理集群级别的一些变更,例如新建或删除索引.增加或移除节点等.主节点 ...

  4. Winform控件WebBrowser与JS脚本交互

    1)在c#中调用js函数 如果要传值,则可以定义object[]数组. 具体方法如下例子: 首先在js中定义被c#调用的方法: function Messageaa(message) {       ...

  5. centos7 时间设置

    安装完成centos7后,虽然时区选择的是上海,但是最终的时间还是不对,因为没有开启自动同步NTP功能,所以需要自动手动设置. 首先输入timedatectl命令,查看当前机器的时间: Local t ...

  6. Qt for Mac 安装(包括PyQt)

    下载Qt: http://qt-project.org/downloads Qt5.2 for Mac:http://download.qt-project.org/official_releases ...

  7. 温故而知新 css + html 超级牛逼的居中策略

    该方法甚至可以解决img内容居中的问题 套路:最外层div宽度为居中内容所占的宽度(通常是1170px),并且使其居中(margin:auto) 里层的div宽度为全屏(通常是1920px;)再mar ...

  8. sql编写将时间转换年月日 时分格式

    SELECT SUBSTRING(CONVERT(varchar(100),时间字段, 22),0,15) AS aa  FROM  表名

  9. 【实战Java高并发程序设计 1】Java中的指针:Unsafe类

    是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...

  10. IOS网络第七天WebView-02WebView和网页的交互2,删除大众点评多余文字,加上蒙版进度

    ************ #import "HMViewController.h" @interface HMViewController () <UIWebViewDele ...