canvas第一天

扫码查看

第一次接触canvas,<canvas></canvas>是html5出现的新标签,IE9开始支持,像所有的dom对象一样它有自己本身的属性、方法和事件,其中就有绘图的方法,js能够调用它来进行绘图。使用的领域涉及到小游戏的开发以及数据的可视化。

一、两个概念:

1、 绘图环境的含义:

绘制图形所需的东西,就叫绘图环境。

2、 路径的含义:

路径是一个图形的轮廓,是对将来要绘制图形的一个规划。如果要让路径最终生效需要描边。

二、使用canvas绘制图形

canvas要展示的绘制效果是通过js来绘制的。

1、准备工作:

(1)需要获取一个绘图环境:

通过canvasDOM元素提供的一个方法,getContext。

var cvs = document.querySelector('canvas');

(2)获取绘图环境的方法:

canvas.getContext( '2d' || 'webgl' )   传入2d代表获取一个2d绘图环境,传入3d代表获取一个3d绘图环境。

var ctx = cvs.getContext('2d');

2、绘制图形

(1)先移动钢笔到指定的位置

ctx.moveTo( 0, 0 );

(2)画图形的路径( 绘制矩形 )

ctx.lineTo(100, 0);
             ctx.lineTo(100, 100);
             ctx.lineTo(0, 100);
             ctx.lineTo(0, 0);

(3)描边

ctx.stroke();

三、画布设置

1、 画布的默认设置:

canvas画布默认大小为300*150。

2、 动态设置画布的大小:

(1)canvasDOM对象有一个width和height属性,通过修改这两个属性值,就可以动态设置画布的大小。因为canvas绘制的图形是位图(像素图),默认就是基于像素单位的(px),所以不用加单位。

例:cvs.width = 500;

cvs.height = 500;

(2)动态设置画布大小会清除画布的内容,绘制图形之后,动态修改画布的大小,绘自动清除画布已经绘图的内容。

(3)不要设置canvas样式中的宽高,因为会拉伸画布,原理和img设置样式宽高拉伸图片一样。直接设置<canvas width=" " height=" "></canvas>

四、基于状态

canvas绘图环境中的很多方法也是基于状态的,即修改了canvas绘图环境中的某些属性值, 相关连的一些方法最终的执行结果可能就会收到改变。

案例:

function Person( name, age ) {
this.name = name;
this.age = age;
} // 这个方法就是基于状态(属性值)的
Person.prototype.run = function() {
if( this.age < 5 ) {
console.log('爬着跑');
}else if( this.age >=5 && this.age < 16 ) {
console.log('跳着跑');
}else if( this.age >=16 && this.age < 48 ) {
console.log('慢跑');
}else {
console.log('拄着拐杖跑');
}
}; var xiaofang = new Person('小芳', 12);
var fangma = new Person('小芳妈', 37);
var fangnai = new Person('小芳奶', 60); xiaofang.run();
fangma.run();
fangnai.run();

五、描边色、填充、填充色及设置线宽

1、 设置描边色

(1) ctx.strokeStyle = css支持的所有颜色表示方式,这里都支持。

当修改了这个描边色之后,一些和描边相关的方法,再次调用时都会受到影响。

(2)设置 描边色后要调动stroke()方法,新的图形才会显示。

(3)多次绘制图形描边会把前面的覆盖。

(4)解决上面出现的问题:清除路径

ctx.beginPath();

2、填充 填充色

(1)填充:ctx.fill();

(2)设置填充色:ctx.fillStyle=css

3、设置线宽

(1)ctx.lineWidth=number;  注意:不用加"px"

(2)设置线宽后,顶端会出现锯齿,防止锯齿出现:闭合路径。

绘制完路径后,调用closePath()方法,有了这个方法后,最后一条路径可以不用绘制了。

六、 非零环绕原则

1、作用:

用来判断路径围起来的图形,是在路径内,还是路径外。

2、原理:

(1) 在路径包含的区域内随便找一点,向外发送一条线,让这条线贯穿所有围绕该点的路径即可

(2)开始一个计数器,初始值为0

(3)开始计数,遇到顺时针围绕点的路径,数+1,遇到逆时针围绕点的路径,数-1

(4)最终看看结果,如果不是0,那么认为这个块区域在路径包含之内

七、 清除画布

ctx.clearRect(起点x轴坐标,起点y轴坐标,清除区域的宽,清除区域的高);

清除整个画布: ctx.clearRect(0 , 0 , cvs.width , cvs.height);

八、小属性及虚线绘制介绍

1、 线帽样式:

ctx.lineCap = 'butt' || 'round' || 'square'     默认值为butt

round用来设置圆头(圆的半径是线宽的一半),square是两段个加长线宽的一半。

2、 焦点样式:

ctx.lineJoin = 'miter' || 'round' || 'bevel'     默认值为miter

设置箭头长度,该属性只在lineJoin为miter的时候有效:ctx.miterLimit

3、 虚线

(1)设置虚线样式:

ctx.setLineDash([  ]);  注意:数组的长度是任意的,现实后空;可以传入一个参数,此时实空的长度一样。

(2)获取虚线样式:

ctx.getLineDash();

九、 画弧路径:

ctx.arc( 圆心x轴坐标,圆心y轴坐标,半径,弧的起始位置,弧的结束位置,是否逆时针画(可选) )     默认是顺时针画弧。

案例:

<canvas style="border: 1px solid red" width="500" height="500"></canvas>
<script>
var cvs = document.querySelector('canvas');
var ctx = cvs.getContext('2d'); // 角度转换为弧度
function angleToRadian( angle ) {
return Math.PI / 180 * angle;
} // 顺时针从0度到90度画弧
ctx.arc( 100, 100, 50, angleToRadian(0), angleToRadian(90) );
ctx.stroke(); // 逆时针从0度到90度画弧
ctx.beginPath();
ctx.arc( 300, 100, 50, angleToRadian(0), angleToRadian(90), true );
ctx.stroke();

十、 封装

1、 面向对象封装等腰三角形

        // 绘制等腰三角形的方法
function triangle( x, y, width, height, strokeStyle ) {
/*
* 实现思路:
* 1、为了防止重绘之前的路径,先清除一下
* 2、移动钢笔到图形起点
* 3、画想要图形的路径
* 4、设置描边色
* 5、描边
* */
ctx.beginPath();
ctx.moveTo( x, y );
ctx.lineTo( x + width / 2, y + height );
ctx.lineTo( x - width / 2, y + height );
ctx.lineTo( x, y );
ctx.strokeStyle = strokeStyle;
ctx.stroke();
} triangle( 100, 100, 100, 50, 'green' );

2、 面向对象封装

// 等腰三角形的构造函数
function Triangle( x, y, width, height, strokeStyle ) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.strokeStyle = strokeStyle;
} // 根据实例的属性绘制
Triangle.prototype.draw = function() {
/*
* 实现思路:
* 1、为了防止重绘之前的路径,先清除一下
* 2、移动钢笔到图形起点
* 3、画想要图形的路径
* 4、设置描边色
* 5、描边
* */
ctx.beginPath();
ctx.moveTo( this.x, this.y );
ctx.lineTo( this.x + this.width / 2, this.y + this.height );
ctx.lineTo( this.x - this.width / 2, this.y + this.height );
ctx.lineTo( this.x, this.y );
ctx.strokeStyle = this.strokeStyle;
ctx.stroke();
}; var triangle1 = new SanJiaoXing( 100, 100, 100, 50, 'green' ); triangle1.draw();

十一、 绘制坐标系

 /*
* constructor { LineChart } 折线图构造函数
* param { ctx: Context } 绘图上下文
* param { paddingArr: Array } 折线图到画布四边的距离,存储顺序为上右下左
* param { arrowArr: Array } 折线图中箭头的宽和高
* */
function LineChart( ctx, paddingArr, arrowArr ) {
this.ctx = ctx;
this.paddingArr = paddingArr; this.arrowArr = arrowArr;
this.arrowWidth = this.arrowArr[0];
this.arrowHeight = this.arrowArr[1]; // 计算上顶点的坐标
this.vertexTop = {
x: this.paddingArr[ 3 ],
y: this.paddingArr[ 0 ]
}; // 计算原点的坐标
this.origin = {
x: this.paddingArr[ 3 ],
y: this.ctx.canvas.height - this.paddingArr[ 2 ]
}; // 计算右顶点的坐标
this.vertexRight = {
x: this.ctx.canvas.width - this.paddingArr[ 1 ],
y: this.ctx.canvas.height - this.paddingArr[ 2 ]
};
} // 置换原型
LineChart.prototype = { constructor: LineChart, // 绘制坐标轴中的两条线
drawLine: function() {
this.ctx.beginPath();
this.ctx.moveTo( this.vertexTop.x, this.vertexTop.y );
this.ctx.lineTo( this.origin.x, this.origin.y );
this.ctx.lineTo( this.vertexRight.x, this.vertexRight.y );
this.ctx.stroke();
}, // 绘制坐标轴中的两个箭头
drawArrow: function() { // 先绘制上面箭头
this.ctx.beginPath();
this.ctx.moveTo( this.vertexTop.x, this.vertexTop.y );
this.ctx.lineTo( this.vertexTop.x - this.arrowWidth / 2, this.vertexTop.y + this.arrowHeight );
this.ctx.lineTo( this.vertexTop.x, this.vertexTop.y + this.arrowHeight / 2 );
this.ctx.lineTo( this.vertexTop.x + this.arrowWidth / 2, this.vertexTop.y + this.arrowHeight );
this.ctx.closePath();
this.ctx.stroke(); // 再绘制右面箭头
this.ctx.beginPath();
this.ctx.moveTo( this.vertexRight.x, this.vertexRight.y );
this.ctx.lineTo( this.vertexRight.x - this.arrowHeight, this.vertexRight.y - this.arrowWidth / 2 );
this.ctx.lineTo( this.vertexRight.x - this.arrowHeight / 2, this.vertexRight.y );
this.ctx.lineTo( this.vertexRight.x - this.arrowHeight, this.vertexRight.y + this.arrowWidth / 2 );
this.ctx.closePath();
this.ctx.stroke();
}
}; var lineChart = new LineChart( ctx, [ 20, 20, 20, 20 ], [ 10, 20 ] );
lineChart.drawLine();
lineChart.drawArrow();

十二、 绘制折线图

/*
* constructor { LineChart } 折线图构造函数
* param { ctx: Context } 绘图上下文
* param { paddingArr: Array } 折线图到画布四边的距离,存储顺序为上右下左
* param { arrowArr: Array } 折线图中箭头的宽和高
* param { data: Array } 存储了折线图中所需的数据
* */
function LineChart( ctx, paddingArr, arrowArr, data ) {
this.ctx = ctx;
this.paddingArr = paddingArr; this.arrowArr = arrowArr;
this.arrowWidth = this.arrowArr[0];
this.arrowHeight = this.arrowArr[1]; this.data = data; // 计算上顶点的坐标
this.vertexTop = {
x: this.paddingArr[ 3 ],
y: this.paddingArr[ 0 ]
}; // 计算原点的坐标
this.origin = {
x: this.paddingArr[ 3 ],
y: this.ctx.canvas.height - this.paddingArr[ 2 ]
}; // 计算右顶点的坐标
this.vertexRight = {
x: this.ctx.canvas.width - this.paddingArr[ 1 ],
y: this.ctx.canvas.height - this.paddingArr[ 2 ]
}; // 根据数据得到对应的坐标
this.processData();
} // 置换原型
LineChart.prototype = { constructor: LineChart, // 绘制折线图
draw: function() {
this.drawCoordinate();
this.drawArrow();
this.drawPoint();
this.drawLine();
}, // 绘制坐标轴中的两条线
drawCoordinate: function() {
this.ctx.beginPath();
this.ctx.moveTo( this.vertexTop.x, this.vertexTop.y );
this.ctx.lineTo( this.origin.x, this.origin.y );
this.ctx.lineTo( this.vertexRight.x, this.vertexRight.y );
this.ctx.stroke();
}, // 绘制坐标轴中的两个箭头
drawArrow: function() { // 先绘制上面箭头
this.ctx.beginPath();
this.ctx.moveTo( this.vertexTop.x, this.vertexTop.y );
this.ctx.lineTo( this.vertexTop.x - this.arrowWidth / 2, this.vertexTop.y + this.arrowHeight );
this.ctx.lineTo( this.vertexTop.x, this.vertexTop.y + this.arrowHeight / 2 );
this.ctx.lineTo( this.vertexTop.x + this.arrowWidth / 2, this.vertexTop.y + this.arrowHeight );
this.ctx.closePath();
this.ctx.stroke(); // 再绘制右面箭头
this.ctx.beginPath();
this.ctx.moveTo( this.vertexRight.x, this.vertexRight.y );
this.ctx.lineTo( this.vertexRight.x - this.arrowHeight, this.vertexRight.y - this.arrowWidth / 2 );
this.ctx.lineTo( this.vertexRight.x - this.arrowHeight / 2, this.vertexRight.y );
this.ctx.lineTo( this.vertexRight.x - this.arrowHeight, this.vertexRight.y + this.arrowWidth / 2 );
this.ctx.closePath();
this.ctx.stroke();
}, // 把传入进来的数据转化为对应画布的坐标
processData: function() { // 用来存储转换后的坐标数据
this.processArr = []; // 遍历所有的数据,依次转换为对应的坐标
for( var i = 0, len = this.data.length; i < len; i+=2 ) {
/*
* 数据转化为相当于画布的坐标:
* canvasX = this.origin.x + x
* canvasY = this.origin.y - y
* */
this.processArr.push( this.origin.x + this.data[ i ] );
this.processArr.push( this.origin.y - this.data[ i + 1 ] );
}
}, // 根据数据绘制相应的点
drawPoint: function() {
var r = 4; // 遍历所有的坐标,依次绘制点
for( var i = 0, len = this.processArr.length; i < len; i+=2 ) {
this.ctx.beginPath();
this.ctx.arc( this.processArr[ i ], this.processArr[ i + 1 ], r, 0, Math.PI*2 );
this.ctx.fill();
}
}, // 根据数据绘制折线
drawLine: function() {
this.ctx.beginPath();
for( var i = 0, len = this.processArr.length; i < len; i+=2 ) {
this.ctx.lineTo( this.processArr[ i ], this.processArr[ i + 1 ] );
}
this.ctx.stroke();
}
}; var lineChart = new LineChart( ctx, [ 20, 20, 20, 20 ], [ 10, 20 ], [ 10, 10, 30, 20, 50, 50, 60, 80, 100, 100 ] );
lineChart.draw();

总结:

canvas第一天-LMLPHP

05-11 15:25
查看更多