1.canvas标签的简单使用
<body>
<!-- canvas是一个用来展示绘图效果的标签,它和img类似,都是行内块元素 -->
<!-- 同一个页面可以拥有多个canvas标签 -->
<!-- 画布默认300*150,要设置画布的宽高,需要借助canvas元素的属性 -->
<canvas id="cvs" width="600" height="600"></canvas>
<script>
var cvs = document.getElementById('cvs');
/*
* 上面的canvas只是用来展示绘图效果的,
* 那么这个图从哪里来的呢?
* 需要打开canvas(通过getContext方法来打开),然后在上面绘图图形,
* 最终就可以透过canvas标签看到。
* */
var ctx = cvs.getContext('2d');
/*
* canvas绘图的几个步骤:
* 1、先移动钢笔到指定的位置
* 2、开始画线条
* 3、描边路径
* */
// 1、先移动钢笔到指定的位置
// ctx.moveTo( x轴移动的坐标,y轴移动的坐标 )
ctx.moveTo(10, 10);
// 2、开始画线条
// ctx.lineTo( x轴移动的坐标,y轴移动的坐标 )
ctx.lineTo(110, 10);
ctx.lineTo(110, 110);
ctx.lineTo(10, 110);
// 下面这句等价于ctx.closePath();
ctx.lineTo(10, 10);
// 颜色设置,必须放在绘制之前
ctx.strokeStyle = 'blue';
// 线宽设置,必须放在绘制之前
ctx.lineWidth = 2;
// 3、描边路径
ctx.stroke();
</script>
</body>
显示效果:为一个蓝色矩形
2.
// 放了防止重绘之前的路径,所以先把之前的路径清除掉
this.ctx.beginPath();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<canvas id="cvs"></canvas>
<script>
var cvs = document.getElementById('cvs');
var ctx = cvs.getContext('2d');
// 构造函数
function MyRect( ctx, startX, startY, width, height, lineWidth, strokeStyle ) {
this.ctx = ctx;
this.startX = startX;
this.startY = startY;
this.width = width;
this.height = height;
this.lineWidth = lineWidth;
this.strokeStyle = strokeStyle;
}
// 给原型添加一个绘制方法
MyRect.prototype.draw = function() {
**// 放了防止重绘之前的路径,所以先把之前的路径清除掉
this.ctx.beginPath();**
this.ctx.moveTo( this.startX, this.startY );
this.ctx.lineTo( this.startX + this.width, this.startY );
this.ctx.lineTo( this.startX + this.width, this.startY + this.height );
this.ctx.lineTo( this.startX, this.startY + this.height );
this.ctx.closePath();
this.ctx.lineWidth = this.lineWidth;
this.ctx.strokeStyle = this.strokeStyle;
this.ctx.stroke();
}
var myRect = new MyRect( ctx, 10, 10, 60, 60, 4, 'deeppink' );
myRect.draw();
var myRect2 = new MyRect( ctx, 100, 10, 100, 100, 6, 'skyblue' );
myRect2.draw();
</script>
</body>
</html>
3.非零环绕原则
这个原理其实很简单,发一个链接,大家去看下,Canvas中的非零环绕规则原理
先看这个链接的最后一个例子,画同心圆的时候,用的方法context.arc
的语法:
context.arc(x,y,r,sAngle,eAngle,counterclockwise);
参数 描述
x 圆的中心的 x 坐标。
y 圆的中心的 y 坐标。
r 圆的半径。
sAngle 起始角,以弧度计。(弧的圆形的三点钟位置是 0 度)。
eAngle 结束角,以弧度计。
counterclockwise 可选。规定应该逆时针还是顺时针绘图。
False = 顺时针,true = 逆时针。
这个例子中,outer圆是true逆时针画的,inner圆是false顺时针画的,先在圆的边框上画出对应的箭头,
那么,我们根据非零环绕原则:
在从1区域画的线,与其相交的两个曲线,一个顺时针,一个逆时针,结果为0,所以1区域不会被fill(填充)。
再看2区域,只有一条曲线与其相交,逆时针的线,结果为-1,所以2区域会被填充,就是这么easy了。如果画举行,你是怎么样一步一步lineTo,lineTo的就决定了画法的方向。
尝试下画一个中空的矩形吧:
<body>
<canvas id="cvs"></canvas>
<script>
var cvs = document.getElementById('cvs');
var ctx = cvs.getContext('2d');
// 封装一个绘制矩形的函数
function MyRect(startX, startY, width, height, lineWidth, strokeStyle) {
ctx.moveTo(startX, startY);
ctx.lineTo(startX + width, startY);
ctx.lineTo(startX + width, startY + height);
ctx.lineTo(startX, startY + height);
ctx.closePath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = strokeStyle;
ctx.stroke();
}
// 顺时针画一个大矩形
MyRect(10, 10, 100, 100, 6, 'skyblue');
//这一句不需要了 ctx.beginPath();
// 逆时针画一个小矩形
ctx.moveTo(35, 35);
ctx.lineTo(35, 55);
ctx.lineTo(55, 55);
ctx.lineTo(55, 35);
ctx.closePath();
ctx.lineWidth = 4;
ctx.strokeStyle = 'blue';
// 利用非0环绕原则原则
ctx.fillStyle = 'yellow';
// 一起填充
ctx.fill();
</script>
</body>
4.fill( ) 和 stroke( )方法的调用顺序问题
在sublime中跑一下就看出区别了
<script>
var cvs = document.getElementById('cvs');
var ctx = cvs.getContext('2d');
// 封装一个绘制矩形的函数
function juXing( startX, startY, width, height, lineWidth, strokeStyle, fillStyle ) {
ctx.moveTo( startX, startY );
ctx.lineTo( startX + width, startY );
ctx.lineTo( startX + width, startY + height );
ctx.lineTo( startX, startY + height );
ctx.closePath();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = strokeStyle;
ctx.fillStyle = fillStyle;
ctx.fill();
ctx.stroke();
}
/*
* 描边的时候,会占用原图形的一部分( 线宽的一半 )。
* 所以,日常开发中,为了让线宽符合要求,
* 最好先填充,再描边,防止填充时覆盖掉线宽的一半。
* */
</script>
5.线帽样式
<body>
<canvas id="cvs" width="500" height="500"></canvas>
<script>
var cvs = document.getElementById('cvs');
var ctx = cvs.getContext('2d');
/*
* 设置线帽样式:
* ctx.lineCap = ‘butt' 、'round'、'square'
* butt是默认值,
* round线头是圆的,
* square线头两段各增加线宽的一半。
* */
ctx.lineWidth = 10;
// 默认线头
ctx.moveTo( 10, 10 );
ctx.lineTo( 310, 10 );
ctx.stroke();
// 增长线头,两端各增长线宽的一半
ctx.beginPath();
ctx.lineCap = 'square';
ctx.moveTo( 10, 30 );
ctx.lineTo( 310, 30 );
ctx.stroke();
// 圆线头,两端的圆半径为线宽的一半
ctx.beginPath();
ctx.lineCap = 'round';
ctx.moveTo( 10, 50 );
ctx.lineTo( 310, 50 );
ctx.stroke();
</script>
</body>
6.线交点样式
<body>
<canvas id="cvs" width="500" height="500"></canvas>
<script>
var cvs = document.getElementById('cvs');
var ctx = cvs.getContext('2d');
/*
* 设置线帽样式:
* ctx.lineJoin = ‘miter' 、'round'、'bevel'
* miter是默认值,两边向外延伸相交为尖尖角,
* round是圆头,
* bevel两边相连为一个斜面。
* */
ctx.lineWidth = 10;
// 默认交点为尖尖角
ctx.lineJoin = 'miter';
ctx.moveTo( 10, 10 );
ctx.lineTo( 60, 110 );
ctx.lineTo( 110, 10 );
ctx.stroke();
// 交点为圆头
ctx.beginPath();
ctx.lineJoin = 'round';
ctx.moveTo( 10, 50 );
ctx.lineTo( 60, 150 );
ctx.lineTo( 110, 50 );
ctx.stroke();
// 交点为斜面
ctx.beginPath();
ctx.lineJoin = 'bevel';
ctx.moveTo( 10, 90 );
ctx.lineTo( 60, 190 );
ctx.lineTo( 110, 90 );
ctx.stroke();
</script>
</body>
7.用清除画布的方法让画布移动起来
<body>
<canvas id="cvs" width="500" height="500">
</canvas>
<script>
var cvs = document.getElementById('cvs');
var ctx = cvs.getContext('2d');
function Rect(ctx, originX, originY, width, height, linewidth, strokestyle) {
this.ctx = ctx;
this.originX = originX;
this.originY = originY;
this.width = width;
this.height = height;
this.linewidth = linewidth;
this.strokestyle = strokestyle;
}
Rect.prototype.draw = function() {
this.ctx.beginPath();
this.ctx.moveTo(this.originX, this.originY);
this.ctx.lineTo(this.originX + this.width, this.originY);
this.ctx.lineTo(this.originX + this.width, this.originY + this.height);
this.ctx.lineTo(this.originX, this.originY + this.height);
this.ctx.closePath();
this.ctx.lineWidth = this.linewidth;
this.ctx.strokeStyle = this.strokestyle;
this.ctx.stroke();
}
var rect = new Rect(ctx, 10, 10, 60, 60, 4, 'deeppink');
rect.draw();
setInterval(function() {
// 把画布上的内容都清空
ctx.clearRect(0, 0, cvs.width, cvs.height);
rect.originX += 2;
rect.draw();
}, 0.5);
</script>
</body>
8.绘制虚线ctx.setLineDash( )和ctx.lineDashOffset( )
知识点:
<script>
/*
* 数组map方法,es5提供的
* 语法: 数组.map( function( val, index, arr ) { return 1; } )
* 返回值:把回调reurn的值共同组成一个数组
* */
var arr = [ 1, 2, 3, 4, 5];
var newArr = arr.map( function( val, index ) {
return val * 10;
});
console.log( newArr );
</script>
----------
<canvas id="cvs"></canvas>
<script>
var cvs = document.getElementById('cvs');
var ctx = cvs.getContext('2d');
/*
* 设置画线的时候空白部分和实线部分的大小。
* ctx.setLineDash( [ 5, 3 ] )
* */
ctx.lineDashOffset = 3;
ctx.setLineDash( [ 4, 3, 2 ] );
ctx.moveTo( 10, 10 );
ctx.lineTo( 310, 10 );
ctx.stroke();
/*
* 获取线条绘制规则。
* ctx.getLineDash()
* */
console.log(ctx.getLineDash());
/*
* 设置虚线绘制时的偏移量
* ctx.lineDashOffset = 3;
* */
</script>
9.画一个坐标轴,画出箭头,画几个点,并连线起来
<body>
<canvas id="cvs" width="500" height="500"></canvas>
<script>
var cvs = document.getElementById('cvs');
var ctx = cvs.getContext('2d');
ctx.lineWidth = 2;
// 坐标轴距离画布上右下左的边距
var padding = {
top: 20,
right: 20,
bottom: 20,
left: 20
}
// 坐标轴中箭头的宽和高
var arrow = {
width: 12,
height: 20
}
// 求坐标轴上顶点的坐标
var vertexTop = {
x: padding.left,
y: padding.top
}
// 求坐标轴原点的坐标
var origin = {
x: padding.left,
y: cvs.height - padding.bottom
}
// 求坐标轴右顶点的坐标
var vertexRight = {
x: cvs.width - padding.right,
y: cvs.height - padding.bottom
}
// 画坐标轴中的两条线
ctx.moveTo( vertexTop.x, vertexTop.y );
ctx.lineTo( origin.x, origin.y );
ctx.lineTo( vertexRight.x, vertexRight.y );
ctx.stroke();
// 画上顶点箭头
ctx.beginPath();
ctx.moveTo( vertexTop.x, vertexTop.y );
ctx.lineTo( vertexTop.x - arrow.width / 2, vertexTop.y + arrow.height );
ctx.lineTo( vertexTop.x, vertexTop.y + arrow.height / 2 );
ctx.lineTo( vertexTop.x + arrow.width / 2, vertexTop.y + arrow.height );
ctx.closePath();
ctx.fill();
// 画右顶点箭头
ctx.beginPath();
ctx.moveTo( vertexRight.x, vertexRight.y );
ctx.lineTo( vertexRight.x - arrow.height, vertexRight.y - arrow.width / 2 );
ctx.lineTo( vertexRight.x - arrow.height / 2, vertexRight.y );
ctx.lineTo( vertexRight.x - arrow.height, vertexRight.y + arrow.width / 2 );
ctx.closePath();
ctx.fill();
/*
* 在坐标轴中指定位置画点,坐标算法:
* 点的x轴:原点x坐标 + 点到原点的水平距离
* 点的y轴:原点y坐标 - 点到原点的垂直距离
* */
// 需求,利用折线图的方式展示一下门口大爷酱香饼每日销售量
// [ 10, 20, 50, 80, 120, 300, 100, 50, 2 ];
var data = [ 10, 20, 50, 80, 120, 300, 100, 50, 2 ];
// 画点
data.forEach( function( val, index ) {
console.log(val,index);
ctx.fillRect( origin.x + index * 20 - 2, origin.y - val - 2, 4, 4 );
});
// 画折线
ctx.beginPath();
data.forEach( function( val, index ) {
ctx.lineTo( origin.x + index * 20, origin.y - val );
});
ctx.stroke();
</script>
</body>