1.canvas的介绍
标签是 HTML 5 中的新标签。 标签用于绘制图像(通过脚本,通常是 JavaScript)。不过, 元素本身并没有绘制能力(它仅仅是图形的容器),必须使用脚本来完成实际的绘图任务。getContext() 方法可返回一个对象,该对象提供了用于在画布上绘图的方法和属性。主要使用 getContext(“2d”) 对象属性和方法,用于在画布上绘制文本、线条、矩形、圆形等等。
2.简单的举例
Html:
<canvas id=“canvas”></canvas>
JavaScript:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = “green”;//填充
ctx.fillRect(10, 10, 100, 100);//矩形左上角坐标(相对画布左上角),宽度,高度,需要注意的是,设置样式等应写在绘制图形之前,否则样式会渲染不上。
3.常用的绘制图形
HTML中的元素canvas支持一种原生的图形绘制较少(矩形、圆形等)。所有其他的图形的绘制都至少需要生成一条路径。图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。一个路径,甚至一个子路径,都是闭合的。使用路径绘制图形需要一些额外的步骤。
1. 首先,你需要创建路径起始点。
2. 然后你使用画图命令去画出路径。
3. 之后你把路径封闭。
4. 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。
常用的的函数:
beginPath()新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
closePath()闭合路径之后图形绘制命令又重新指向到上下文中。
stroke()通过线条来绘制图形轮廓。
fill()通过填充路径的内容区域生成实心的图形。
—①矩形—
canvas提供了三种方法绘制矩形:
fillRect(x, y, width, height)绘制一个填充的矩形
strokeRect(x, y, width, height)绘制一个矩形的边框
clearRect(x, y, width, height)清除指定矩形区域,让清除部分完全透明。
上面提供的方法之中每一个都包含了相同的参数。x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。width和height设置矩形的尺寸。
例如:
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.fillRect(25,25,100,100);
ctx.clearRect(45,45,60,60);
ctx.strokeRect(50,50,50,50);
}
fillRect()函数绘制了一个边长为100px的黑色正方形。clearRect()函数从正方形的中心开始擦除了一个60*60px的正方形,接着strokeRect()在清除区域内生成一个50*50的正方形边框。
—②圆形—
绘制圆或者圆弧,使用arc()方法。
arc(x, y, radius, startAngle, endAngle, anticlockwise)
画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。
注意:arc()函数中的角度单位是弧度,不是度数。角度与弧度的js表达式:radians=(Math.PI/180)*degrees。
例如:
var ctx=document.getElementById("canvas").getContext("2d");
ctx.beginPath();
ctx.arc(200,200,100,0,360*Math.PI/180,true);
ctx.closePath();
ctx.stroke();
—③三角形—
var canvas = document.getElementById('canvas');
if (canvas.getContext)//通过简单的测试getContext()方法的存在,脚本可以检查浏览器编程支持性
{
var ctx = canvas.getContext('2d');
ctx.beginPath(); //创建一个新的路径
ctx.moveTo(75,50); //画笔的起始点坐标
ctx.lineTo(100,75);//绘制直线
ctx.lineTo(100,25);//绘制直线
ctx.closePath();//关闭路径
ctx.fill(); //填充
}
注:当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你可以不调用closePath()函数。但是调用stroke()时不会自动闭合。
– – -移动笔触(moveTo())— — —–
在画三角形的时候就可以看出,moveTo()是一个非常有用的函数,而这个函数实际上并不能画出任何东西,但moveTo(x, y)可以将笔触移动到指定的坐标x以及y上,由于大多数复杂的图形并不能一笔勾画完整,所以需要重新选择画笔的位置,moveTo()方法即可实现该需求。
例如:
var canvas = document.getElementById("canvas");
if (canvas.getContext){
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(75,75,50,0,Math.PI*2,true); // 绘制外圆
ctx.moveTo(110,75);
ctx.arc(75,75,35,0,Math.PI,false); // 口(顺时针)
ctx.moveTo(65,65);
ctx.arc(60,65,5,0,Math.PI*2,true); // 左眼
ctx.moveTo(95,65);
ctx.arc(90,65,5,0,Math.PI*2,true); // 右眼
ctx.closePath();
ctx.stroke();
}
—4.绘制文本—
canvas 提供了两种方法来渲染文本:
fillText(text, x, y [, maxWidth])在指定的(x,y)位置填充指定的文
本,绘制的最大宽度是可选的.
strokeText(text, x, y [, maxWidth])在指定的(x,y)位置绘制
文本边框,绘制的最大宽度是可选的.
设置文本的属性可以改变canvas显示文本的方式:
font = value当前我们用来绘制文本的样式. 这个字符串使用和 CSS font 属性相同的语法. 默认的字体是 10px sans-serif。
textAlign = value文本对齐选项. 可选的值包括:start, end, left, right or center. 默认值是 start。textBaseline = value基线对齐选项. 可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默认值是 alphabetic。
direction = value文本方向。可能的值包ltr, rtl, inherit。默认值是 inherit。
例如:
var ctx =document.getElementById('canvas').getContext('2d');
ctx.font = "48px serif";
ctx.fillText("Hello world", 10, 50);
ctx.strokeText("Hello world", 50, 100);
—5. canvas的应用实例—
本实例主要实现的功能有:
1. 在图片上绘制图形标记
2. 图片上的标记不随浏览器窗口大小缩放而改变其位置
3. 给图片上的标记绑定点击事件
在html中首先创建一个隐藏的div放置待处理图片,再创建一个新的画布(bridge),将画布放置在id为div_out_1的模块中。
<body onload="init()" >
<div style="display: none;">
<img src="duanmian19.png" id="duanmian2">
</div>
<div id="div_out_1" >
<canvas id="bridge" ></canvas>
</div>
</body>
接下来就是使用JavaScript绘制图片,在HTML加载的时候,可以看到body中有init()的初始化函数 ,初始化中由于要绘制多个标记,故在js里以数组形式定义了各个标记的位置。
//矩形点击框
<script>
//矩形点击框
arr = [
{x:85, y:181, width:30, height:13},
{x:265, y:181, width:30, height:13},
{x:470, y:181, width:30, height:13},
{x:675, y:181, width:30, height:13},
{x:915, y:181, width:30, height:13},
{x:1035, y:181, width:30, height:13}
];
//矩形包含框
rect_arr=[
{x:85, y:10, width:30, height:130},
{x:265, y:10, width:30, height:130},
{x:470, y:10, width:30, height:130},
{x:675, y:10, width:30, height:130},
{x:915, y:10, width:30, height:130},
{x:1035, y:10, width:30, height:130}
];
//文本显示
arr_string = [
{name:'A',x:95, y:210},
{name:'B',x:275, y:210},
{name:'C',x:480, y:210},
{name:'D',x:685, y:210},
{name:'E',x:925, y:210},
{name:'F',x:1045, y:210}
];
function init(){
var canvas=document.getElementById("bridge");//获取画布对象
var ctx = canvas.getContext("2d");//画笔
//获取画布所占的像素
canvas.width = document.getElementById("div_out_1").offsetWidth;
canvas.height = document.getElementById("div_out_1").offsetHeight;
var image= document.getElementById("duanmian2");
//计算画布除以图片大小的比例
width_ration = (canvas.width) / image.width;
height_ration = (canvas.height) / image.height;
//清空画布
ctx.clearRect(0, 0, (canvas.width) / width_ration, (canvas.height) / height_ration);
ctx.scale(width_ration, height_ration);
drawbackimg(ctx);//重绘画布
arr.forEach(function(v){
ctx.beginPath();//重绘可点击的矩形框
ctx.lineWidth="1";
ctx.strokeStyle="red";
ctx.rect(v.x, v.y, v.width, v.height);
ctx.closePath();
ctx.stroke();
});
arr_string.forEach(function(v){//添加ABCD等标示
ctx.font="16px Georgia";
ctx.fillStyle="#ff0000";
ctx.fillText(v.name,v.x, v.y);
});
canvas.addEventListener('click', function(e){//给矩形框绑定点击事件
p = getEventPosition(e);//获取点击的坐标
console.log(p);
var index=draw(canvas,ctx,p);
redraw(ctx,index);//根据返回的序号绘制相应的模块
}, false);
}
function drawbackimg(ctx){
//重绘图片
var img=document.getElementById("duanmian2");
ctx.beginPath();
ctx.drawImage(img,0,0);
ctx.closePath();
ctx.stroke();
}
function getEventPosition(ev){
var x, y;
if (ev.offsetX || ev.offsetX == 0) {
x = ev.offsetX;
y = ev.offsetY;
}else if (ev.layerX || ev.layerX == 0) {
x = ev.layerX;
y = ev.layerY;
}
return {x: x, y: y};
}
function draw(canvas,ctx,p){
var who = [];
//清空画布
ctx.clearRect(0, 0, (canvas.width) / width_ration, (canvas.height) / height_ration);
drawbackimg(ctx);
//调整点击的坐标(相对画布缩放比例)
p.x = (p.x) / width_ration;
p.y = (p.y) / height_ration;
console.log("trnamsfor");
console.log(p.x);
console.log(p.y);
//添加A、B、C等标示
arr_string.forEach(function(v){
ctx.font="16px Georgia";
ctx.fillStyle="#ff0000";
ctx.fillText(v.name,v.x, v.y);
});
arr.forEach(function(v, i){
ctx.beginPath();
ctx.rect(v.x, v.y, v.width, v.height);
ctx.closePath();
ctx.stroke();
//利用if(p && ctx.isPointInPath(p.x,p.y))无法正确执行,原因未明,以下方式是判断点击的坐标是否在某个矩形框内,若在,则返回该矩形框的序号,并加入数组who
if ((p.x > v.x) && (p.x < v.x + v.width) && (p.y > v.y) && (p.y < v.y + v.height)) {
who.push(i);
}
});
//根据数组中的index值,可以到arr数组中找到相应的元素。
return who;
}
function redraw(ctx,i){
//在断面上重绘可点击的矩形框
ctx.beginPath();
ctx.fillStyle="red";
ctx.fillRect(arr[i].x, arr[i].y, arr[i].width, arr[i].height);
ctx.closePath();
ctx.stroke();
//在断面上绘制包含截面的矩形框
ctx.beginPath();
ctx.lineWidth=4;
ctx.strokeStyle="red";
ctx.strokeRect(rect_arr[i].x,rect_arr[i].y,rect_arr[i].width,rect_arr[i].height);
ctx.closePath();
ctx.stroke();
// refresh line width
ctx.lineWidth=1;
}
</script>
注:各个浏览器对于捕捉鼠标当前坐标的位置方式不同,因而支持的方法也不同。
在谷歌浏览器中,offset和layer都能获取值,但二者的值会有区别,layer的值会随该坐标所在div的缩放而变化,offset则不会(在div不缩放的情况下,layer和offset的值是相同的),IE浏览器同理。而Firefox不支持offset方式。
1. 在点击坐标获取的方法中, offsetX和LayerX的判断顺序使得该段程序在Firefox中可以正常运行,但是在IE和Google Chrome中不能运行。
offsetX和layerX
offsetX:鼠标相比较于触发事件的元素的位置,以元素盒子模型的内容区域的左上角为参考点,如果有boder,可能出现负值。
layerX:鼠标相比较于当前坐标系的位置,即如果触发元素没有设置绝对定位或相对定位,以页面为参考点,如果有,将改变参考坐标系,从触发元素盒子模型的border区域的左上角为参考点也就是当触发元素设置了相对或者绝对定位后,layerX和offsetX就几乎相等,唯一不同就是一个从border为参考点,一个以内容为参考点。
layer和offset在chrome中都是获取鼠标相对于元素原点的位置,这里的原点是以border原点计算的。但是当使用transform进行2D变换时,这两者的区别就显现出来了。一个会受变换影响而另一个则不会。
例如,一个DIV宽高都是100px。
我们将鼠标移动到这个矩形的中心,得到的数据为 layer(50,50) offset(50,50)。
如果给这个DIV进行一次2D变换添加scale(0.5),即缩小50%,那么此时我们再将鼠标移动到这个矩形的中心我们得到的数据为 layer(25,25) offset(50,50)
参考:
1. https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Basic_usage
2. http://www.bkjia.com/jQuery/979178.html
3. http://bz5811.iteye.com/blog/1911666
4. http://www.cnblogs.com/ghost-xyx/p/3830528.html
5. http://www.xyhtml5.com/html5-canvas-event-handler.html