使用canvas进行图像编辑
前面的话
本文将分为几个小功能的形式来详细介绍canvas图像编辑
缩放
下面是一张分析图,假设默认情况下,图片和canvas宽高相同。图片的缩放(scale)范围为0.5到3,缩放时改变的是图片的大小和图片的坐标位置
W(宽) = canvas.width * scale
H(高) = canvas.height * scale
x坐标 = (W - canvas.width)/2;
y坐标 = (H - canvas.height)/2;
因此,代码如下
<canvas id="drawing" style="border:1px solid black">
<p>The canvas element is not supported!</p>
</canvas>
<br>
<input id="scale-range" min="0.5" max="1.5" step="0.01" type="range" style="width:300px;">
<script>
var drawing = document.getElementById('drawing');
if(drawing.getContext){
var context = drawing.getContext('2d');
var slider = document.getElementById('scale-range');
var W = 400;
var H = 290;
drawing.width = W;
drawing.height = H;
var image = new Image();
image.src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/chunfen.jpg";
image.onload = function(){
drawImgByScale(slider.value);
slider.onmousemove = function(){
drawImgByScale(slider.value);
}
}
function drawImgByScale(scale){
var imgW = W * scale;
var imgH = H * scale;
var dx =(W - imgW)/2;
var dy =(H - imgH)/2;
context.clearRect(0,0,W,H);
context.drawImage(image,dx,dy,imgW,imgH);
}
}
</script>
水印
利用canvas可以实现向图片添加水印的功能,先通过file控件的reader选择图片,然后使用canvas添加图片及水印,并且使用toDataURL()和a标签实现添加水印后的图片的下载功能
<canvas id="drawing" style="width:300px;border:1px solid black">
<p>The canvas element is not supported!</p>
</canvas>
<div>
<span>
<input type="file" id="addImgHelper" style="display:none">
<button id="addImg">选择图片</button>
</span>
<span>
<button id="addWaterMark" disabled>添加水印</button>
<span>水印文字为</span>
<input id="waterMarkWords" type="text" value="小火柴的蓝色理想">
</span>
<span>
<button id="downloadImg" disabled>下载图片</button>
<a id="downloadImgHelper" href="#" download="带水印图片" style="display:none"></a>
</span>
</div>
<script>
if(drawing.getContext){
var cxt = drawing.getContext('2d');
var W,H;
addImg.onclick = function(){
addImgHelper.click();
}
addImgHelper.onchange = function(){
addWaterMark.disabled = true;
downloadImg.disabled = true;
var file = addImgHelper.files[0];
if(file && /image/.test(file.type)){
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(){
var img = new Image();
img.src= reader.result;
img.onload = function(){
addWaterMark.disabled = false;
drawing.width = W = img.width;
drawing.height = H = img.height;
cxt.drawImage(img,0,0);
addWaterMark.onclick = function(){
downloadImg.disabled = false;
cxt.clearRect(0,0,W,H);
cxt.drawImage(img,0,0);
var str = waterMarkWords.value;
cxt.font = "bold 50px Arial";
cxt.lineWidth = '1';
cxt.fillStyle = 'rgba(255,255,255,0.5)';
cxt.textBaseline = "bottom";
cxt.textAlign = 'end';
cxt.fillText(str,W-10,H-10,W/2);
downloadImg.onclick = function(){
downloadImgHelper.href = drawing.toDataURL('image/png');
downloadImgHelper.click();
}
}
}
}
}
}
}
</script>
放大镜
下面来实现一个放大镜的效果,鼠标按下并移动时,显示当前图片区域的放大效果,抬起后效果消失。放大镜效果主要使用离屏canvas的技术,离屏canvas放置的是图片的放大版,而普通canvas则放置图片的正常版
<canvas id="drawing" style="border:1px solid black">
<p>The canvas element is not supported!</p>
</canvas>
<canvas id="drawingOff" style="display:none">
<p>The canvas element is not supported!</p>
</canvas>
<script>
if(drawing.getContext){
var cxt = drawing.getContext('2d');
var cxtOff = drawingOff.getContext('2d');
var W,H;
var scale = 1.5;
var img = new Image();
img.src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/chunfen.jpg";
img.onload = function(){
W = img.width;
H = img.height;
drawing.width = W/scale;
drawing.height = H/scale;
drawingOff.width = W;
drawingOff.height = H;
cxt.drawImage(img,0,0,W/scale,H/scale);
cxtOff.drawImage(img,0,0);
drawing.onmousedown = function(e){
e = e || event;
var x0 = this.offsetLeft;
var y0 = this.offsetTop;
drawMagnifier(e);
drawing.onmousemove = function(e){
drawMagnifier(e);
}
document.onmouseup = function(e){
cxt.clearRect(0,0,W/scale,H/scale);
cxt.drawImage(img,0,0,W/scale,H/scale);
drawing.onmousemove = null;
}
function drawMagnifier(e){
cxt.clearRect(0,0,W/scale,H/scale);
cxt.drawImage(img,0,0,W/scale,H/scale);
var x = (e.clientX-x0);
var y = (e.clientY-y0);
var r = 40;
var dx = x - r;
var dy = y - r;
var sx = x*scale - r;
var sy = y*scale - r;
cxt.save();
cxt.beginPath();
cxt.arc(x,y,r,0,Math.PI*2);
cxt.lineWidth = 4;
cxt.strokeStyle = '#069';
cxt.stroke();
cxt.clip();
cxt.drawImage(drawingOff,sx,sy,2*r,2*r,dx,dy,2*r,2*r);
cxt.restore();
}
}
}
}
</script>
按下鼠标后不松开,即可出现放大镜效果
滤镜
下面利用canvas的getImageData()方法,获取原始图像数据,通过对图像数据进行修改,然后输出修改后的图像数据
<canvas id="drawing1" style="border:1px solid black">
<p>The canvas element is not supported!</p>
</canvas>
<canvas id="drawing2" style="border:1px solid black">
<p>The canvas element is not supported!</p>
</canvas>
<br>
<button id="noGreen">无绿色</button>
<button id="noBlue">无蓝色</button>
<button id="toGrey">灰度</button>
<button id="toBlackWhite">黑白</button>
<button id="reverse">反色</button>
<script>
if(drawing1.getContext){
var cxt1 = drawing1.getContext('2d');
var cxt2 = drawing2.getContext('2d');
var img = new Image();
img.src="chunfen.jpg";
img.onload = function(){
cxt1.drawImage(img,0,0);
function filter(fn){
var imageData = cxt1.getImageData(0,0,img.width,img.height);
cxt2.clearRect(0,0,drawing2.width,drawing2.height);
var data = imageData.data;
for(var i = 0, len = data.length; i < len; i+=4){
fn(data,i)
}
imageData.data = data;
cxt2.putImageData(imageData,0,0);
}
function fnNoGreen(data,i){
data[i+1] = 0;
}
function fnNoBlue(data,i){
data[i+2] = 0;
}
function fnReverse(data,i){
var red = data[i];
var green = data[i+1];
var blue = data[i+2];
var alpha = data[i+3];
data[i] = 255 - red;
data[i+1] = 255 - green;
data[i+2] = 255 - blue;
}
function fnToGrey(data,i){
var red = data[i];
var green = data[i+1];
var blue = data[i+2];
var alpha = data[i+3];
var average = Math.floor((red+green+blue)/3);
data[i] = data[i+1] = data[i+2] = average;
}
function fnToBlackWhite(data,i){
var red = data[i];
var green = data[i+1];
var blue = data[i+2];
var alpha = data[i+3];
var average = Math.floor((red+green+blue)/3);
if(average > 255/2){
var result = 255;
}else{
var result = 0;
}
data[i] = data[i+1] = data[i+2] = result;
}
toGrey.onclick = function(){
filter(fnToGrey);
}
noGreen.onclick = function(){
filter(fnNoGreen);
}
noBlue.onclick = function(){
filter(fnNoBlue);
}
toBlackWhite.onclick = function(){
filter(fnToBlackWhite);
}
reverse.onclick = function(){
filter(fnReverse);
}
}
}
</script>
马赛克效果
【普通模糊效果】
普通模糊效果不仅需要使用当前像素点,还需要使用周围的像素点,并把这些像素点都赋予平均值
function fnToBlur(n){
cxt2.clearRect(0,0,drawing2.width,drawing2.height);
var imageData = cxt1.getImageData(0,0,drawing2.width,drawing2.height);
var tempImageData = imageData;
var data = imageData.data;
var tempData = tempImageData.data;
var blurR = n;
var totalnum = (2*blurR + 1)*(2*blurR + 1);
for(var i = blurR; i < drawing2.height - blurR; i++){
for(var j = blurR; j < drawing2.width - blurR; j++){
var totalr = 0, totalg = 0, totalb = 0;
for(var dx = -blurR; dx <= blurR; dx++){
for(var dy = -blurR; dy <= blurR; dy++){
var x = i + dx;
var y = j + dy;
var p = x*drawing2.width + y;
totalr += tempData[p*4+0];
totalg += tempData[p*4+1];
totalb += tempData[p*4+2];
}
}
var p = i*drawing2.width + j;
data[p*4+0] = totalr / totalnum;
data[p*4+1] = totalg / totalnum;
data[p*4+2] = totalb / totalnum;
}
}
imageData.data = data;
cxt2.putImageData(imageData,0,0);
}
【马赛克效果】
马赛克效果则是把一块区域的值,全部都赋予平均值
function fnToMosaic(n){
cxt2.clearRect(0,0,drawing2.width,drawing2.height);
var imageData = cxt1.getImageData(0,0,drawing2.width,drawing2.height);
var tempImageData = imageData;
var data = imageData.data;
var tempData = tempImageData.data;
var size = n;
var totalnum = size*size;
for(var i = 0; i < drawing2.height; i+=size){
for(var j = 0; j < drawing2.width; j+=size){
var totalr = 0, totalg = 0, totalb = 0;
for(var dx = 0; dx < size; dx++){
for(var dy = 0; dy < size; dy++){
var x = i + dx;
var y = j + dy;
var p = x*drawing2.width + y;
totalr += tempData[p*4+0];
totalg += tempData[p*4+1];
totalb += tempData[p*4+2];
}
}
var p = i*drawing2.width + j;
var resr = totalr / totalnum;
var resg = totalg / totalnum;
var resb = totalb / totalnum;
for(var dx = 0; dx < size; dx++){
for(var dy = 0; dy < size; dy++){
var x = i + dx;
var y = j + dy;
var p = x*drawing2.width + y;
data[p*4+0]= resr;
data[p*4+1]= resg;
data[p*4+2]= resb;
}
}
}
}
imageData.data = data;
cxt2.putImageData(imageData,0,0);
}
下面是一个实例
<canvas id="drawing1" style="border:1px solid black">
<p>The canvas element is not supported!</p>
</canvas>
<canvas id="drawing2" style="border:1px solid black">
<p>The canvas element is not supported!</p>
</canvas>
<br>
<button id="toLightBlur">轻度模糊</button>
<button id="toHeavyBlur">重度模糊</button>
<button id="toLightMosaic">轻度马赛克</button>
<button id="toHeavyMosaic">重度马赛克</button>
<script>
if(drawing1.getContext){
var cxt1 = drawing1.getContext('2d');
var cxt2 = drawing2.getContext('2d');
var img = new Image();
img.src="chunfen.jpg";
img.onload = function(){
cxt1.drawImage(img,0,0);
toLightBlur.onclick = function(){
fnToBlur(1);
}
toHeavyBlur.onclick = function(){
fnToBlur(3);
}
toLightMosaic.onclick = function(){
fnToMosaic(4);
}
toHeavyMosaic.onclick = function(){
fnToMosaic(9);
}
function fnToBlur(n){
cxt2.clearRect(0,0,drawing2.width,drawing2.height);
var imageData = cxt1.getImageData(0,0,drawing2.width,drawing2.height);
var tempImageData = imageData;
var data = imageData.data;
var tempData = tempImageData.data;
var blurR = n;
var totalnum = (2*blurR + 1)*(2*blurR + 1);
for(var i = blurR; i < drawing2.height - blurR; i++){
for(var j = blurR; j < drawing2.width - blurR; j++){
var totalr = 0, totalg = 0, totalb = 0;
for(var dx = -blurR; dx <= blurR; dx++){
for(var dy = -blurR; dy <= blurR; dy++){
var x = i + dx;
var y = j + dy;
var p = x*drawing2.width + y;
totalr += tempData[p*4+0];
totalg += tempData[p*4+1];
totalb += tempData[p*4+2];
}
}
var p = i*drawing2.width + j;
data[p*4+0] = totalr / totalnum;
data[p*4+1] = totalg / totalnum;
data[p*4+2] = totalb / totalnum;
}
}
imageData.data = data;
cxt2.putImageData(imageData,0,0);
}
function fnToMosaic(n){
cxt2.clearRect(0,0,drawing2.width,drawing2.height);
var imageData = cxt1.getImageData(0,0,drawing2.width,drawing2.height);
var tempImageData = imageData;
var data = imageData.data;
var tempData = tempImageData.data;
var size = n;
var totalnum = size*size;
for(var i = 0; i < drawing2.height; i+=size){
for(var j = 0; j < drawing2.width; j+=size){
var totalr = 0, totalg = 0, totalb = 0;
for(var dx = 0; dx < size; dx++){
for(var dy = 0; dy < size; dy++){
var x = i + dx;
var y = j + dy;
var p = x*drawing2.width + y;
totalr += tempData[p*4+0];
totalg += tempData[p*4+1];
totalb += tempData[p*4+2];
}
}
var p = i*drawing2.width + j;
var resr = totalr / totalnum;
var resg = totalg / totalnum;
var resb = totalb / totalnum;
for(var dx = 0; dx < size; dx++){
for(var dy = 0; dy < size; dy++){
var x = i + dx;
var y = j + dy;
var p = x*drawing2.width + y;
data[p*4+0]= resr;
data[p*4+1]= resg;
data[p*4+2]= resb;
}
}
}
}
imageData.data = data;
cxt2.putImageData(imageData,0,0);
}
}
}
</script>
使用canvas进行图像编辑的更多相关文章
- HTML5 程序设计 - 使用HTML5 Canvas API
请你跟着本篇示例代码实现每个示例,30分钟后,你会高喊:“HTML5 Canvas?!在哥面前,那都不是事儿!” 呵呵.不要被滚动条吓到,很多都是代码和图片.我没有分开写,不过上面给大家提供了目录,方 ...
- html5 Canvas API
详细内容请点击 1.HTML Canvas API有两方面优势可以弥补:首先,不需要将所绘制图像中的每个图元当做对象存储,因此执行性能非常好:其次,在其他编程语言现有的优秀二维绘图API的基础上实现C ...
- Canvas简述
HTML Canvas API有两方面优势可以弥补:首先,不需要将所绘制图像中的每个图元当做对象存储,因此执行性能非常好:其次,在其他编程语言现有的优秀二维绘图API的基础上实现Canvas API相 ...
- canvas知识01
本文转自:http://www.cnblogs.com/jsdarkhorse/archive/2012/06/29/2568451.html 更多参考:http://www.cnblogs.com/ ...
- html5 canvas常用api总结(三)--图像变换API
canvas的图像变换api,可以帮助我们更加方便的绘画出一些酷炫的效果,也可以用来制作动画.接下来将总结一下canvas的变换方法,文末有一个例子来更加深刻的了解和利用这几个api. 1.画布旋转a ...
- 【探索】利用 canvas 实现数据压缩
前言 HTTP 支持 GZip 压缩,可节省不少传输资源.但遗憾的是,只有下载才有,上传并不支持.如果上传也能压缩,那就完美了.特别适合大量文本提交的场合,比如博客园,就是很好的例子. 虽然标准不支持 ...
- 简单入门canvas - 通过刮奖效果来学习
一 .前言 一直在做PC端的前端开发,从互联网到行业软件.最近发现移动端已经成为前端必备技能了,真是不能停止学习.HTML5新增的一些东西,canvas是用的比较多也比较复杂的一个,简单的入门了一下, ...
- 获取Canvas当前坐标系矩阵
前言 在我的另一篇博文 Canvas坐标系转换 中,我们知道了所有的平移缩放旋转操作都会影响到画布坐标系.那在我们对画布进行了一系列操作之后,怎么再知道当前矩阵数据状态呢. 具体代码 首先请看下面的一 ...
- Canvas坐标系转换
默认坐标系与当前坐标系 canvas中的坐标是从左上角开始的,x轴沿着水平方向(按像素)向右延伸,y轴沿垂直方向向下延伸.左上角坐标为x=0,y=0的点称作原点.在默认坐标系中,每一个点的坐标都是直接 ...
随机推荐
- C# datatable 去重
若检索出的datatab有重复行,而在绑定下拉列表时不希望有重复行,可使用以下代码将数据表去重: this.cmbE_NENRYOU_SBT.DataSource = dt.DefaultView.T ...
- JS操作数组常用的方法
JS操作Array对象的方法 concat(arr1,arr2,...):连接数组indexOf(value):返回数组中value的第一个索引join(separator):将数组中所有的元素连接由 ...
- 如何写出面试官欣赏的Java单例
单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例. 今天我们不谈单例模式的用途,只说一说如果在面试的时候面试官让你敲一段代码 ...
- (转)eclipse报错及解决说明 "XX cannot be resolved to a type "
场景:在项目开发时,一个工程引用另一个工程中的文件,出现报错“XX cannot be resolved to a type”,但是在实际通过跟踪“F3”能够找到相应的文件,最终用方法4解决. 引言: ...
- 大数据与Java的关系
随着2017年大数据各种应用的发展,大数据的价值得以充分的发挥,大数据已在企业.社会各个层面都成为重要的手段,数据已成为新的企业战略制高点,也是各个企业争夺的新焦点.那么我们一直在说着的大数据究竟是什 ...
- JStorm与Storm源码分析(五)--SpoutOutputCollector与代理模式
本文主要是解析SpoutOutputCollector源码,顺便分析该类中所涉及的设计模式–代理模式. 首先介绍一下Spout输出收集器接口–ISpoutOutputCollector,该接口主要声明 ...
- Filter ,Interceptor,AOP
一.Filter: Filter也称之为过滤器,它是Servlet技术中比较激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态 ...
- 蓝桥杯比赛javaB组练习《牌型种数》
牌型种数 小明被劫持到X赌城,被迫与其他3人玩牌.一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张.这时,小明脑子里突然冒出一个问题:如果不考虑花色,只考虑点数,也不考虑自己得到的牌 ...
- log4g 使用教程
日志是应用软件中不可缺少的部分,Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录.在apache网站:jakarta.apache.org/log4j 可以免费下载到Log ...
- Ubuntu 16.04 LTS安装 TeamViewer
Ubuntu 16.04 LTS安装 TeamViewer 64位Ubuntu 16.04系统需要添加32位架构支持,命令如下. sudo dpkg --add-architecture i3 ...