一、图像绘制

canvas绘制图像的方法是ctx.drawImage();该方法有三种使用方式

1、ctx.drawImage(img,x,y);

img是指图像对象,x和y分别是这个图像左上角在canvas画布的坐标,而图像显示的大小为图像本身的像素值,超出canvas画布的部分不显示。

2、ctx.drawImage(img,x,y,w,h);

和上面的那个函数相比,可以控制图像在canvas中显示的宽度和高度,而不再是图片自身的高宽像素

3、ctx.drawImage(img , dx , dy , dw , dh , x , y , w , h);

这个又比之前的函数多了dx、dy、dw、dh这四个参数,这四个参数可以控制图片切片显示在canvas画布中。在图片上同样以左上角为原点,向右为X轴正方向,向下为Y轴正方向,这四个参数表示,在图片中在(dx,dy)这个坐标,切dw和dh大小的切片。然后将这个切片显示在canvas画布上。

最后这个img参数,不单单可以是Image对象,还可以是video对象,也可以是另外的canvas对象。所以可以通过该方法对video视频进行截屏,也可以通过加载canvas制作图像水印、放大镜、以及淘宝双11主会场那样的特效,一个canvas加载另一个canvas,也被称为canvas的离屏技术。

二、图像的像素级处理

1、ctx.getImageData(x , y , w , h);

这个方法是获取画布(x,y)坐标,宽高分别为w和h的像素对象。而这个对象的data属性,则是其rgba的值。一个画布的像素值是从画布的原点,从左向右,从上到下,一排一排的排列下来的。常用取出canvas像素值的方法有两种,一种是采用一层循环,另一种是采用双重for循环,代码如下:

var imageData = ctx.getImageData(0 , 0 , canvasW , canvasH),
pixelData = imageData.data;
//方式一
for(var i = 0; i < canvasH*canvasW ; i++){
var r = pixelData[4*i + 0],
g = pixelData[4*i + 1],
b = pixelData[4*i + 2],
a = pixelData[4*i + 3];
}
//方式二
for(var y = 0 ; i < canvasH ; y++){
for(var x = 0 ; x < canvasW ; x++){
var pixe = canvasW*y + x,
r = pixelData[4*pixe + 0],
g = pixelData[4*pixe + 1],
b = pixelData[4*pixe + 2],
a = pixelData[4*pixe + 3];
}
}

2、ctx.putImageData()

该方法是设置canvas画布上的像素数据,它有七个参数,在w3c上是这么解释的

我的理解就是将imgData,放在画布上的位置是(x + dirtyX , y + dirtyY),高宽分别是dirtyHeight、dirtyWidth的矩形。

三、canvas图像下载

使用的是canvas对象的toDataURL()方法,它可以将canvas画布中的内容转化为浏览器可下载的本地data数据,然后通过href跳转的方式完成下载,代码如下

var canvas = document.getElementById("canvas");
var imgUrl = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
window.location.href = imgUrl;

四、代码demo

1、图片缩放,以及像素变换

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>图像学习</title>
<style>
#main,#copy,.contain{
margin: 15px;
padding: 0;
float: left; }
#main{
cursor:move
}
.contain *{
padding: 2px;
margin: 0;
z }
.contain a{
display: block;
text-align: center;
margin-top:5px;
font-size:15px;
line-height: 20px;
}
</style>
</head>
<body>
<canvas id="main" width="400" height="300" style="display:block;border: 1px solid #AAA;">
亲,您的浏览器不支持canvas
</canvas>
<div class="contain" style="width: 290px;height: 300px;">
<div>
<span>缩放</span>
<input type="range" id="scaleChange" min="0.5" max="4.0" step="0.03" value="1.0"
style="display:inline-block;width: 240px;"/>
</div>
<a href="javascript:void(0)" id="greyChange">灰度转换</a>
<a href="javascript:void(0)" id="blackChange">黑度转换</a>
<a href="javascript:void(0)" id="reverChange">反相转换</a>
<a href="javascript:void(0)" id="blurChange">模糊处理</a>
<a href="javascript:void(0)" id="mosaicChange">马赛克处理</a>
<a href="javascript:void(0)" id="downLoadImage">下载右侧图片</a>
</div>
<canvas id="copy" width="400" height="300" style="display:block;border: 1px solid #AAA;">
亲,您的浏览器不支持canvas
</canvas>
<canvas id="waterMark" style="display: none;">
亲,您的浏览器不支持canvas
<!--
水印作图的canvas,把一个canvas载入另一个canvas的做法叫做,离屏canvas
-->
</canvas>
</body>
<script>
document.body.onload = function(){
var canvas = document.getElementById("main"),
scaleChange = document.getElementById("scaleChange"),
copyCanvas = document.getElementById("copy"),
waterMarkCanvas = document.getElementById("waterMark"),
ctx = canvas.getContext("2d"),
copyCtx = copyCanvas.getContext("2d"),
canvasW = canvas.width,
canvasH = canvas.height;
var img = new Image();
img.src = "img.jpg";
img.onload = function(){ //当图片加载完毕后,执行canvas画图函数
init();
}
//用于初始化
function init(){
toolFunc.handleWaterMark();
toolFunc.handleScale();
toolFunc.drawImg();
toolFunc.handleEffect();
toolFunc.handleCanvasMove();
}
var toolFunc = {
scaleImgX : 0,
scaleImgY : 0,
drawImg : function(moveX , moveY){ //绘制图片
var scale = scaleChange.value;
//根据比例计算缩放后图像的大小
var imgW = canvasW*scale,
imgH = canvasH*scale,
x = (canvasW - imgW)/2,
y = (canvasH - imgH)/2,
tempX = x , tempY = y;
if(moveX || moveY){
x = this.scaleImgX + moveX;
y = this.scaleImgY + moveY;
if(x >= 0 || y >= 0 || x <= 2*tempX || y <= 2*tempY){
return;
}
}
ctx.clearRect(0 , 0 , canvasW , canvasH);
ctx.drawImage(img , x , y , imgW , imgH);
ctx.drawImage(waterMarkCanvas ,
canvasW - waterMarkCanvas.width ,
canvasH - waterMarkCanvas.height ,
waterMarkCanvas.width , waterMarkCanvas.height);
this.scaleImgX = x;
this.scaleImgY = y;
},
handleWaterMark : function(){ //用于处理水印
waterMarkCanvas.width = 200;
waterMarkCanvas.height = 70;
var waterCtx = waterMarkCanvas.getContext("2d");
waterCtx.font = "bold 20px Arial";
waterCtx.lineWidth = 1;
waterCtx.fillStyle = "white";
waterCtx.textAlign = "center";
waterCtx.textBaseline = "middle";
waterCtx.fillText( "~!~ sky ~!~" , 140 , 50 , waterMarkCanvas.width);
},
handleScale : function(){ //用于处理缩放
var isScale = false,
_this = this;
scaleChange.onmousedown = function(){
isScale = true;
}
scaleChange.onmousemove = function(){
if(isScale){ //画图
_this.drawImg();
}
}
scaleChange.onmouseup = function(){
isScale = false;
}
},
handleEffect : function(){ //处理效果变换
var imageData,pixelData;
document.getElementById("greyChange").onclick = function(){ //灰度变换
imageData = ctx.getImageData(0 , 0 , canvasW , canvasH);
pixelData = imageData.data;
for(var i = 0; i < canvasH*canvasW ; i++){
var r = pixelData[4*i + 0],
g = pixelData[4*i + 1],
b = pixelData[4*i + 2],
grey = r*0.3+g*0.59+b*0.11;//此为灰度图像的算法
pixelData[4*i + 0] = grey;
pixelData[4*i + 1] = grey;
pixelData[4*i + 2] = grey;
}
copyCtx.putImageData(imageData,0,0,0,0,canvasW,canvasH);
}
document.getElementById("blackChange").onclick = function(){ //黑度变换
imageData = ctx.getImageData(0 , 0 , canvasW , canvasH);
pixelData = imageData.data;
for(var i = 0; i < canvasH*canvasW ; i++){
var r = pixelData[4*i + 0],
g = pixelData[4*i + 1],
b = pixelData[4*i + 2],
grey = (function(n){ //黑度图像就只有黑白两色
if(n > 127){
return 255;
}else{
return 0;
}
})(r*0.3+g*0.59+b*0.11);
pixelData[4*i + 0] = grey;
pixelData[4*i + 1] = grey;
pixelData[4*i + 2] = grey;
}
copyCtx.putImageData(imageData,0,0,0,0,canvasW,canvasH);
}
document.getElementById("reverChange").onclick = function(){ //反向变换
imageData = ctx.getImageData(0 , 0 , canvasW , canvasH);
pixelData = imageData.data;
for(var i = 0; i < canvasH*canvasW ; i++){
var r = pixelData[4*i + 0],
g = pixelData[4*i + 1],
b = pixelData[4*i + 2];
pixelData[4*i + 0] = 255 - r;
pixelData[4*i + 1] = 255 - g;
pixelData[4*i + 2] = 255 - b;
}
copyCtx.putImageData(imageData,0,0,0,0,canvasW,canvasH);
}
document.getElementById("blurChange").onclick = function(){ //模糊处理
imageData = ctx.getImageData(0 , 0 , canvasW , canvasH);
pixelData = imageData.data;
var pixelTmpData = pixelData; //作为一个备份数据
var r = 3 , totalNum = (2*r + 1)*(2*r + 1); //去上下左右四个方向各3个像素点的平均值
for(var i = r ; i < canvasH - r ; i++){
for(var j = r ; j < canvasW - r; j++){
var totalR,totalG,totalB;
totalR = totalG = totalB = 0;
for(var m = -r ; m <= r ; m++){
for(var n = -r ; n <= r ; n++){
var pixeY = i + m , piexX = j + n,
pixe = canvasW*pixeY + piexX;
totalR += pixelTmpData[4*pixe + 0];
totalG += pixelTmpData[4*pixe + 1];
totalB += pixelTmpData[4*pixe + 2];
}
}
var p = i*canvasW + j;
pixelData[4*p + 0] = totalR/totalNum;
pixelData[4*p + 1] = totalG/totalNum;
pixelData[4*p + 2] = totalB/totalNum;
}
}
copyCtx.putImageData(imageData,0,0,0,0,canvasW,canvasH);
}
document.getElementById("mosaicChange").onclick = function(){ //马赛克处理
imageData = ctx.getImageData(0 , 0 , canvasW , canvasH);
pixelData = imageData.data;
var pixelTmpData = pixelData; //作为一个备份数据
var size = 8,total = size*size;
for(var i = 0 ; i < canvasH ; i += size){
for( var j = 0 ; j < canvasW ; j += size){
var totalR,totalG,totalB;
totalR = totalG = totalB = 0;
for(var y = 0 ; y < size ; y++){
for(var x = 0 ; x < size ; x++){
var py = i + y,
px = j + x,
pixe = px + py*canvasW;
totalR += pixelTmpData[4*pixe + 0];
totalG += pixelTmpData[4*pixe + 1];
totalB += pixelTmpData[4*pixe + 2];
}
}
var newR = totalR/total,
newG = totalG/total,
newB = totalB/total;
for(var dy = 0 ; dy < size ; dy++){
for(var dx = 0 ; dx < size ; dx++ ){
var py = i + dy,
px = j + dx,
pixe = px + py*canvasW;
pixelData[4*pixe + 0] = newR;
pixelData[4*pixe + 1] = newG;
pixelData[4*pixe + 2] = newB;
}
}
}
}
copyCtx.putImageData(imageData,0,0,0,0,canvasW,canvasH);
}
document.getElementById("downLoadImage").onclick = function(){
var loadUrl = copyCanvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
window.location.href = loadUrl;
}
},
handleCanvasMove:function(){ //处理canvans移动
var isCanvasMove = false,
lastX,lastY;
canvas.onmousedown = function(e){
e.preventDefault();
isCanvasMove = true;
lastX = e.clientX - canvas.getBoundingClientRect().left;
lastY = e.clientY - canvas.getBoundingClientRect().top;
}
canvas.onmousemove = function(e){
e.preventDefault();
if(!isCanvasMove || scaleChange.value <= 1) return;
var x = e.clientX - canvas.getBoundingClientRect().left,
y = e.clientY - canvas.getBoundingClientRect().top;
var moveX = (x - lastX),
moveY = (y - lastY);
toolFunc.drawImg(moveX,moveY);
lastX = x;
lastY = y;
}
canvas.onmouseup = function(e){
e.preventDefault();
isCanvasMove = false;
}
canvas.onmouseout = function(e){
e.preventDefault();
isCanvasMove = false;
}
}
};
}
</script>
</html>

效果图为

用的图片是

2、图片放大镜

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>canvas放大镜</title>
<style type="text/css">
*{
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas id="main" width="800" height="600"
style="display: block;margin: 0 auto;border: 1px solid #AAA;">
亲,您的浏览器不支持canvas
</canvas>
<canvas id="magnify" width="200" height="200" style="display: none;"></canvas>
<script>
var canvas = document.getElementById("main"),
ctx = canvas.getContext("2d"),
canvasW = canvas.width,
canvasH = canvas.height,
magnifyCanvas = document.getElementById("magnify"),
mCtx = magnifyCanvas.getContext("2d"),
magnifyR = 100;
var img = new Image();
img.src = "magnify.jpg";
img.onload = function(){
ctx.drawImage(this , 0 , 0 , canvasW , canvasH);
handleEvent();
}
function handleEvent(){
var isShow = false;
canvas.onmousedown = function(e){
e.preventDefault();
draw(e);
}
canvas.onmouseup = function(e){
e.preventDefault();
draw(e);
}
canvas.onmousemove = function(e){
e.preventDefault();
if(!isShow) return;
draw(e);
}
canvas.onmouseout = function(e){
e.preventDefault();
draw(e);
} function draw(event){
isShow = (event.type == "mousedown" || event.type == "mousemove");
ctx.clearRect(0 , 0 , canvas.width , canvas.height);
ctx.drawImage(img , 0 , 0 , canvasW , canvasH);
if(!isShow) return;
var x = event.clientX - canvas.getBoundingClientRect().left,
y = event.clientY - canvas.getBoundingClientRect().top;
mCtx.save();
mCtx.lineWidth = 3;
mCtx.strokeStyle = "skyblue";
mCtx.beginPath();
mCtx.arc(100,100,90,0,Math.PI*2);
mCtx.stroke();
mCtx.clip();
mCtx.drawImage(img , 2*x - 90 , 2*y - 90 ,
180 , 180,
0 , 0 , magnifyCanvas.width, magnifyCanvas.height);
mCtx.closePath();
mCtx.restore();
ctx.drawImage(magnifyCanvas, (x-(magnifyCanvas.width/2)) , (y-(magnifyCanvas.height/2)) ,
magnifyCanvas.width , magnifyCanvas.height);
}
} </script>
</body>
</html>

用的图片是

canvas基础学习(二)的更多相关文章

  1. Python入门基础学习 二

    Python入门基础学习 二 猜数字小游戏进阶版 修改建议: 猜错的时候程序可以给出提示,告诉用户猜测的数字偏大还是偏小: 没运行一次程序只能猜测一次,应该提供多次机会给用户猜测: 每次运行程序,答案 ...

  2. Python基础学习二

    Python基础学习二 1.编码 utf-8编码:自动将英文保存为1个字符,中文3个字符.ASCll编码被囊括在内. unicode:将所有字符保存为2给字符,容纳了世界上所有的编码. 2.字符串内置 ...

  3. Go基础学习(二)

    数组[array] 数组定义[定义后长度不可变] 12 symbol := [...]string{USD: "$", EUR: "€", GBP: " ...

  4. Django基础学习二

    今天继续学习django的基础 学习用户提交url如何获得返回值 1.首先需要在工程的urls文件定义指定的urls要路由给哪个函数 在这个例子中,我们定义home的urls路由给views里的tes ...

  5. canvas基础学习

    /** * Created by ty on 2016/7/11. * canvas 基础 */ window.onload = function() { var canvas = document. ...

  6. canvas基础学习(一)

    一.概述 canvas它和其它的HTML5标签的使用基本一致,但是它相当于在浏览器中建立一个画布,可以再这个画布上画图.创建动画甚至是3D游戏.由于canvas要适配不同终端的分辨率,所以尽可能的在标 ...

  7. canvas一周一练 -- canvas基础学习

    从上个星期开始,耳朵就一直在生病,里面长了个疙瘩,肿的一碰就疼,不能吃饭不能嗨 (┳_┳)……在此提醒各位小伙伴,最近天气炎热,一定要注意防暑上火,病来如山倒呀~ 接下来我正在喝着5块一颗的药学习ca ...

  8. salesforce lightning零基础学习(二) lightning 知识简单介绍----lightning事件驱动模型

    看此篇博客前或者后,看一下trailhead可以加深印象以及理解的更好:https://trailhead.salesforce.com/modules/lex_dev_lc_basics 做过cla ...

  9. CSS入门基础学习二

    我们下午继续学习CSS的入门基础,搬上你的小板凳赶快进入吧! 一.背景(background) Background-color:背景颜色 background-image (背景图片) backgr ...

随机推荐

  1. 基于CentOS的SSHD服务的Docker镜像

    原文地址 1.Dockerfile文件 FROM registry.aliyuncs.com/acs-sample/centos:6 MAINTAINER xuqh "xqh_163@163 ...

  2. assign,copy,retain的区别以及weak和strong的区别

    @property (nonatomic, assign) NSString *title;    什么是assign,copy,retain之间的区别?      assign: 简单赋值,不更改索 ...

  3. nodejs socket server 强制关闭客户端连接

    nodejs socket server 强制关闭客户端连接: client.destroy()

  4. 算法寒假实习面试经过之 滴滴(电话一面二面 offer)

    一面:1h 介绍比赛项目. lr与xgb的区别? xgb 为什么不用归一化,onehot? xgb 与 gbdt的区别. 做这些比赛你们的优势在哪,既然全是相同的套路. RCNN的原理, CNN的原理 ...

  5. loadrunder之脚本篇——action分类

    Action分类 l . Vuser_init 2. Vuser_end 3.  Action 在lr中用户的初始化操作应该存放在Vuser_init中.用户的结束操作存放在Vuser_end中.因为 ...

  6. Linux yum源码包安装和卸载

    Linux 下的绝大多数源码包都是用 C 语言编写的,还有少部分是用 C++ 等其他程序语言编写的.所以,要想安装源码包,必须安装 C 语言编译器 gcc(如果是用 C++ 编写的程序,则还需要安装 ...

  7. I.MX6Q(TQIMX6Q/TQE9)学习笔记——U-Boot移植

    其实Freescale的BSP移植文档已经将u-boot的移植步骤讲述的非常详细了,但为了以后方便查阅,还是按照自己的理解记录在这里. 获取源码 根据前一篇文章搭建好LTIB环境后就可以非常方便的导出 ...

  8. 内核模块编译时怎样绕过insmod时的版本检查

    1.Uboot:每个arm芯片或者海斯芯片都有各自的uboot. 2.但他们的内核版本可以是一样的,主要是跟各自内核的进行的编译选项有关, 31的内核版本里加了版本检查选项“Kernel type-& ...

  9. PCIE phy和控制器

    转:https://wenku.baidu.com/view/a13bc1c20722192e4436f617.html 文章中的第11页开始有划分phy和控制器部分....

  10. imx6q 添加intel PCIE网卡

    TQ_IMX6Q开发板移植rtl8168-PCIE转千兆网卡 一.配置内核选项PCIE总线驱动支持 默认的内核配置可能没有把PCIE的总线驱动编入内核,所以需要确认是否把驱动编译到了内核里面. 配置好 ...