(1):画布
一、<canvas>标签
Html5 引入了一个新的 <canvas> 标签,这个标签所代表的区域就好象一块画布,你的所有图形绘制最后都要在这块画布上呈现。有了这个标签,浏览器的图形表现力被极大的提升,Flash
和 SilverLight 有没有感到威胁呢?
新闻链接:Google声称Chrome7浏览器将提速60倍
<canvas>标签的用法非常简单,如下:
< canvas id ="tutorial" width ="150" height ="150" style ="background-color:red;">
你的浏览器不支持 Canvas 标签
</ canvas >
<canvas>标签和普通的 HTML 标签没有多大的区别,你可以设置它的宽度和高度,可以通过
CSS 设置它的背景色、边框样式等等。你可以在 这里
找到关于 <canvas> 标签的更多内容。
标签中间的内容是替换内容,如果用户的浏览器不支持 <canvas> 标签,这段内容就会被显示出来;如果用户的浏览器支持
<canvas> 标签,则这段内容将被忽略。
上面的 <canvas> 代码显示效果如下:
你的浏览器不支持 Canvas 标签
如果你用的是IE浏览器,可能只能看到一个提示;如果你用的是谷歌浏览器或者火狐浏览器,你就可以看到一个红色的方块区域。
二、渲染上下文 Rendering Context
其实光有 <canvas> 标签我们并不能作任何事情,玩过 Windows 编程的同学都知道,在
Windows 里面绘图先要得到一个设备上下文 DC ,在 <canvas> 标签上绘图也需要先得到一个渲染上下文,我们的图形并不是直接画到屏幕上的,而是先画到上下文(Context)上,然后再刷新到屏幕上面的。
题外话: 为什么要整出一个“上下文”这么复杂的概念呢?因为有了上下文对象,我们就可以让各种不同的图形设备在我们眼里面看起都是一个样,我们只需要专注于绘图,其他的工作就让操作系统和浏览器去操心吧,说白了就是把各式各样的具体变成统一的抽象,从而减轻我们的负担。
获取上下文非常简单,只需要如下两行代码:
var canvas = document.getElementById('tutorial');
var ctx = canvas.getContext('2d'); 首先获取 canvas 对象,然后调用
canvas 对象的 getContext 方法,这个方法目前只能传入参数 "2d",不久的将来他可能会支持参数
"3d",你一定明白那意味着什么,让我们期待吧。
getContext 方法返回一个 CanvasRenderingContext2D 对象 ,即渲染上下文对象,你可以在
这里
找到关于它的更多内容,都是一些绘图方法。
三、浏览器支持
除了在那些不支持的浏览器上显示替用内容之外,我们还可以通过脚本的方式来检查浏览器是否支持 canvas
,方法很简单,判断 getContext 函数是否存在即可,代码如下:
var canvas = document.getElementById('tutorial');
if (canvas.getContext){
alert("支持 <canvas> 标签");
} else {
alert("不支持 <canvas> 标签");
}
四、一个小例子
下面将用 HTML5 的绘图功能演示一个上下移动的线条的例子, 具体的代码将在后续内容中给出
你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者 FireFox
浏览器
(2):基础绘图
在前一篇博客中,我们测试了 canvas 标签的用法,并得到了 canvas 标签的渲染上下文对象,
但是并没有用它绘制任何图形。在这一篇中,我们先了解一下HTML5绘图的一些基础概念,然后再来画几个图形玩玩。
一、坐标系
其实只要玩过一点点图形编程的人都知道,电脑上的坐标系和数学上的坐标系稍微有点不同,坐标的原点在绘制区域(这里是Canvas)的左上角,X轴正向朝右,Y轴正向朝下,如下图
二、Stroke 和 Fill
HTML5中将图形分为两大类:
第一类称作 Stroke,我的理解就是轮廓、勾勒或者线条,总之,图形是由线条组成的;
第二类称作 Fill,就是填充区域
上下文对象中有两个绘制矩形的方法,可以让我们很好的理解这两大类图形的区别:
一个是 strokeRect,还有一个是 fillRect
下面的代码分别用这两个方法来绘制矩形,你可以分别点击两个按钮来看看有什么不同,从而理解 stroke
和 fill 的区别
设置画布
< canvas id ="test1" width ="200" height ="200" style =" background-color: grey"> 你的浏览器不支持 < canvas > 标签,请使用 Chrome 浏览器 或者 FireFox 浏览器 </ canvas >
< input type ="button" value ="strokeRect" onclick ="strokeRect();"/>
< input type ="button" value ="fillRect" onclick ="fillRect();"/>
strokeRect
和 fillRect
function strokeRect(){
var canvas = document.getElementById( 'test1' );
var ctx=canvas.getContext( "2d" );
ctx.clearRect( 0 , 0 , 200 , 200 );
ctx.strokeStyle= "blue" ;
ctx.strokeRect( 10 , 10 , 180 , 180 );
}
function fillRect(){
var canvas = document.getElementById( 'test1' );
var ctx=canvas.getContext( "2d" );
ctx.clearRect( 0 , 0 , 200 , 200 );
ctx.fillStyle= "blue" ;
ctx.fillRect( 10 , 10 , 180 , 180 );
}
你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者 FireFox 浏览器
三、颜色
上下文对象有两个属性可以用来设置颜色:strokeStyle 和 fillStyle
strokeStyle 的值决定了你当前要绘制的线条的颜色
fillStyle 的值决定了你当前要填充的区域的颜色
颜色值应该是符合CSS3
颜色值标准 的有效字符串。下面的例子都表示同一种颜色。
//这些 fillStyle 的值均为 '橙色',ctx 是上下文对象
ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255,165,0)";
ctx.fillStyle = "rgba(255,165,0,1)";
关于颜色,以后会有更多的说明。
四、基本绘图
除了上面给出的两个绘制矩形的方法外,上下文对象还有几个方法可以用来绘制一些基本图形,如下:
moveTo (x,y):moveTo方法并不能画出任何东西,它只是将画笔的当前点移动到(x,y)处
lineTo (x,y):从当前点到(x,y)点绘制一条直线。注意:绘制完成后,当前点就变成了(x,y),除非你用
moveTo 方法去改变他
arc (x, y, radius, startAngle, endAngle,
anticlockwise) :绘制一条弧线
quadraticCurveTo (cp1x, cp1y, x, y)
bezierCurveTo (cp1x, cp1y, cp2x, cp2y,
x, y) :这两个方法都是绘制贝叶斯曲线,具体用法看参考手册
rect (x, y, width, height) :绘制一个矩形。注意:
当它被调用时,moveTo 方法会自动被调用,参数为(0,0),于是起始坐标又恢复成初始原点了。
有了直线、弧线、曲线、方形和圆形这几种基本图形,我们就可以组合出更复杂的图形了
五、理解绘制路径 Drawing Path
上一篇文章中说过,我们绘制的图形是先绘制到一个抽象的上下文对象中(其实就是内存中),然后再将上下文对象输出到显示设备上,这个输出到显示设备的过程不需要我们操心。但是有时候我们并不想立刻输出每一次绘制动作,也许我想让一组绘制动作完成以后,再集中一块输出,
比如一个围棋棋盘有19×19条直线组成,正常情况下需要向想显示设备输出19×19次,但是如果我们先暂停向显示设备输出,等在上下文中(内存中)全部绘制完成19×19条直线时,再向显示设备输出,只需要输出一次就可以了。
这种情况在HTML5中叫做绘制路径,它由几个上下文对象的方法组成:
beginPath () :开始路径,意思就是在你调用这个方法后,你绘制的图形就不会再向屏幕输出了,而只是画到了上下文对象中(内存中)
stroke () :将你调用 beginPath 方法以后绘制的所有线条,一次性输出到显示设备上
closePath () :如果你调用 beginPath 方法以后,在上下文对象中进行了一系列的绘制,但是得到的图形是不闭合的,这个方法将会帮你补上最后一条直线,将你的图形闭合起来。
注意 : closePath并不向屏幕输出图形,而只是在上下文对象中补上一条线,这个步骤不是必需的 。
fill () :
如果你的绘制路径组成的图形是封闭的,这个方法将用 fillStyle 设置的颜色填充图形,然后立即向屏幕输出;
如果绘制路径不是封闭的,这个方法会先将图形闭合起来,然后再填充输出。
注意: 所有的 fill
图形,如 fillRect 等,都是立刻向屏幕输出的,他们没有绘制路径这个概念
下面的代码将绘制一个简单的填充三角形。
注意: 绘制三角形的时候,默认的背景色为白色,默认的前景色为黑色 。
设置画布
< canvas id ="test2" width ="200" height ="200" style ="border:1px solid #c3c3c3;"> 你的浏览器不支持 < canvas > 标签,请使用 Chrome 浏览器 或者 FireFox 浏览器 </ canvas >
< input type ="button" value ="画三角" onclick ="drawTri();"/>
< input type ="button" value ="清除" onclick ="clearTri();"/>
绘制三角形
<script type = "text/javascript" >
function drawTri(){
var canvas = document.getElementById( 'test2' );
var ctx=canvas.getContext( "2d" );
ctx.beginPath();
ctx.moveTo( 75 , 50 );
ctx.lineTo( 100 , 75 );
ctx.lineTo( 100 , 25 );
ctx.fill();
}
function clearTri(){
var canvas = document.getElementById( 'test2' );
var ctx=canvas.getContext( "2d" );
ctx.clearRect( 0 , 0 , 200 , 200 );
}
</script>
你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者
FireFox 浏览器
六、半个单位的坐标
这里还要回过头来说说坐标,下面的代码是在画布上绘制网格,点击“画网格”按钮可以看见效果
设置画布
< canvas id ="test3" width ="500" height ="375" style ="border:1px solid #c3c3c3;"> 你的浏览器不支持 < canvas > 标签,请使用 Chrome 浏览器 或者 FireFox 浏览器 </ canvas >
< input type ="button" value ="画网格" onclick ="drawMap();"/>
< input type ="button" value ="清除" onclick ="clearMap();"/>
绘制网格
<script type = "text/javascript" >
function drawMap(){
var canvas = document.getElementById( 'test3' );
var ctx=canvas.getContext( "2d" );
ctx.beginPath();
for (var x = 0.5 ; x < 500 ; x += 10 ) {
ctx.moveTo(x, 0 );
ctx.lineTo(x, 375 );
}
for (var y = 0.5 ; y < 375 ; y += 10 ) {
ctx.moveTo( 0 , y);
ctx.lineTo( 500 , y);
}
ctx.strokeStyle = "#eee" ;
ctx.stroke();
}
function clearMap(){
var canvas = document.getElementById( 'test3' );
var ctx=canvas.getContext( "2d" );
ctx.clearRect( 0 , 0 , 500 , 375 );
}
</script>
你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者
FireFox 浏览器
这段代码中,有一处奇怪的地方,就是坐标循环是从0.5开始的,这是为什么呢?
如下图,假如我想绘制一条从(1,0)到(1,3)的线,由于线的默认宽度是一个像素,所以在我想象中应该绘制成深绿色 的部分,即在坐标
1 两边各占半个像素的宽度。
然而,浏览器的最小单位是一个像素,所以他会向两边扩展,实际绘制出来的浅绿色 的部分,即占用了两个像素的宽度。这样,我们绘制的线条在坐标上就不精确了
如下图,如果我们给出的起始坐标是(1.5,0)和(1.5,3),那么线条的宽度才是正确的一个像素。
七、清空画布
上面给出的两段代码中,我们都用到了清空画布,用到的方法如下:
clearRect(x,y,width,height):
它接受四个参数, x 和 y 指定矩形左上角(相对于原点)的位置,width 和 height 是矩形的宽和高。调用该方法会将给出的矩形区域中所有绘制图形都清空,露出画布的背景
(3):像素和颜色
提示:建议使用 Chrome 浏览器或者 Firefox 浏览器阅读本文,否则文中的样例程序可能无法看到运行效果。
一、理解颜色
我们在电脑屏幕上可以看到色彩斑斓的图像,其实这些图像都是由一个个像素点组成的。那么像素是什么?颜色又是什么呢?(如果您提出这两个问题,您一定是个热爱思考的人)一个像素其实对应着内存中的一组连续的二进制位,由于是二进制位,每个位上的取值当然只能是
0 或者 1 了!这样,这组连续的二进制位就可以由 0 ,1 排列组合出很多种情况,而每一种排列组合就决定了这个像素的一种颜色。先看看下面这幅图
我们可以看到这幅图描述了六个像素点,一共由24个小方框组成。
注意:图中的一个小方框代表一个字节,即8个二进制位。
因此,每个像素点由四个字节组成。图中也分别标出了这四个字节代表的含义:
第一个字节决定像素的红色值
第二个字节决定像素的绿色值
第三个字节决定像素的蓝色值
第四个字节决定像素的透明度值
每一种分颜色值的大小是从 0 到 255(提问:为什么只能到255?) ,透明度的取值:0 代表完全透明,255代表完全不透明
这样,我们就可以用(255,0,0,255)来表示一个纯红色像素
在内存中,他是这样的一个32位的串: 11111111 00000000 00000000 11111111
二、操作像素
了解了颜色和像素的实质,我们就可以对图形进行更加复杂的处理。
可是,HTML5 目前还没有提供类似 setPixel 或者 getPixel 这样直接操作像素点的方法,
但是我们也有办法
就是使用 ImageData 对象:
ImageData对象用来保存图像像素值,它有 width、height和 data 三个属性,其中
data 属性就是一个连续数组,图像的所有像素值其实是保存在 data 里面的。
data 属性保存像素值的方法和我们在前面图片中看到的一模一样:
imageData.data[index*4 +0]
imageData.data[index*4 +1]
imageData.data[index*4 +2]
imageData.data[index*4 +3]
上面取出了 data 数组中连续相邻的四个值,这四个值分别代表了图像中第 index+1 个像素的红色、绿色、蓝色和透明度值的大小。
注意:index 从0 开始,图像中总共有 width * height 个像素,数组中总共保存了 width
* height * 4 个数值
上下文对象 Context 有三个方法用来创建、读取和设置 ImageData 对象,他们分别是
createImageData(width, height):在内存中创建一个指定大小的 ImageData
对象(即像素数组),对象中的像素点都是黑色透明的,即rgba(0,0,0,0)
getImageData(x, y, width, height):返回一个 ImageData 对象,这个
IamgeData 对象中包含了指定区域的像素数组
putImageData(data, x, y):将 ImageData 对象绘制到屏幕的指定区域上
三、一个简单的图像处理例子
上面说了这么多,我们用了解的知识来玩玩图像编程,或许有一天我们就要在 Chrome 中玩 PhotoShop
了。
程序大概是这个样子的:
1、将一幅图片绘制到一个 canvas 元素上,为了不引发安全错误(Security_ERR:DOM
EXCEPTION 18),我用的是我博客顶部的横幅背景图片。你要运行这个例子,可能需要改成自己的图片
2、有四个滑动条,分别代表 GRBA 四个分量
3、拖动滑动条,图像中对应的颜色分量就会增加或者减少
4、如果图像变成透明,就会显示 canvas 元素的背景,我把这个背景设置成了我的头像,呵呵。
思路:其实就是用 getImageData 方法,将你想改变的那一块区域的像素数组取出来,然后根据你拖动的滑动条和滑动条的数值,来更改那一块区域里所有像素对应颜色分量的值。处理完毕后再用
putImageData 方法绘制到画布上,就是这么简单。
下面是代码:
简单的图像处理
<canvas id="test1" width="507" height="348" style="background-image:url(http://images.cnblogs.com/cnblogs_com/myqiao/262115/r_2204793492575248335.jpg)">你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者 FireFox 浏览器</canvas>
红色:<input type="range" min="1" max="100" onchange="colorChange(event,0)"/>
绿色:<input type="range" min="1" max="100" onchange="colorChange(event,1)"/>
蓝色:<input type="range" min="1" max="100" onchange="colorChange(event,2)"/>
透明:<input type="range" min="1" max="100" onchange="colorChange(event,3)"/>
<script type="text/javascript">
//获取上下文对象
var canvas = document.getElementById("test1");
var ctx = canvas.getContext("2d");
//画布的宽度和长度
var width = parseInt(canvas.getAttribute("width"));
var height = parseInt(canvas.getAttribute("height"));
//装入图像
var image = new Image();
image.onload =imageLoaded;
//顶部背景图片
image.src =
"/skins/Valentine/images/banner2.gif";
//用来保存像素数组的变量
var imageData= null ;
function imageLoaded() {
// 将图片画到画布上
ctx.drawImage(image, 0, 0);
//取图像的像素数组
imageData = ctx.getImageData(0, 0, width, height);
}
function colorChange(event,offset){
imageLoaded();
for ( var y = 0; y < imageData.height; y++) {
for (x = 0;x < imageData.width; x++) {
//index 为当前要处理的像素编号
var index = y * imageData.width + x;
//一个像素占四个字节,即 p 为当前指针的位置
var p = index * 4;
//改变当前像素 offset 颜色分量的数值,offset 取值为0-3
var color = imageData.data[p + offset] * event.target.value / 50;
// 颜色值限定在[0..255]
color = Math.min(255, color);
//将改变后的颜色值存回数组
imageData.data[p + offset]=color
}
}
//输出到屏幕
ctx.putImageData(imageData, 0, 0);
}
</script>
你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者 FireFox
浏览器
红色:
绿色:
蓝色:
透明:
四、绘制随机颜色的点
这个例子是在画布上随机选择一个点,然后再给他一个随机的颜色值,其实用到的方法和上面的例子大同小异,就是需求不同罢了。
下面是代码和程序实例:
随机颜色的点
<canvas id="test2" width="300" height="300" style=" background-color: black">你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者 FireFox 浏览器</canvas>
<input type="button" value="画随机点" onclick="interval=setInterval(randomPixel,1);" />
<input type="button" value="停止" onclick="clearInterval(interval);"/>
<input type="button" value="清除" onclick="clearCanvas();"/>
<script type="text/javascript">
//获取上下文对象
var canvas = document.getElementById("test2");
var ctx = canvas.getContext("2d");
//画布的宽度和长度
var width = parseInt(canvas.getAttribute("width"));
var height = parseInt(canvas.getAttribute("height"));
var imageData = ctx.createImageData(width, height);
function randomPixel(){
var x= parseInt(Math.random()*width);
var y= parseInt(Math.random()*height);
var index = y * width + x;
var p = index * 4;
imageData.data[p + 0] = parseInt(Math.random() * 256);
imageData.data[p + 1] = parseInt(Math.random() * 256);
imageData.data[p + 2] = parseInt(Math.random() * 256);
imageData.data[p + 3] =parseInt(Math.random() * 256);
ctx.putImageData(imageData,0,0);
}
function clearCanvas(){
ctx.clearRect(0,0,width,height);
imageData = ctx.createImageData(width, height);
}
</script>
你的浏览器不支持 <canvas>标签,请使用 Chrome 浏览器 或者 FireFox
浏览器
版权声明:本文为原创文章,作者保留所有权利!欢迎转载,转载请注明作者左洸和出处博客园