2021.1.29 Canvas(一)
文章目录
Canvas学习笔记(一)
第一章 Canvas概述
什么是Canvas?
Canvas是HTML5新增的一个元素。Canvas又称为画布。
HTML5 <canvas> 元素用于图形的绘制,但只是图形容器,必须通过脚本 (通常是JavaScript)来完成。
HTML5 Canvas简单来说,就是一门使用JavaScript来操作Canvas元素的技术。使用Canvas元素来绘制图形,需要以下三步:
- 获取Canvas对象
- 获取上下文环境context
- 开始绘制图形
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 1px solid black;
/* 不建议 在样式设置尺寸 而在元素本身设置 */
/* 如果在这里设置宽、高,相当于把原始300*150的画布图片拉大了 */
}
</style>
</head>
<body>
<!-- 1.准备画布 -->
<!-- 1.1 画布是白色的,而且默认300*150 -->
<!-- 1.2 设置画布大小 -->
<canvas width="600" height="400"></canvas>
<!-- 2.准备绘制工具 -->
<scrip>
// 1.获取Canvas对象
var myCanvas = document.querySelector('canvas');
// 2.获取上下文环境context 既获取绘制工具箱
var ctx = myCanvas.getContext('2d');
// 3.开始绘制图形
//3.1 移动画笔
ctx.moveTo(100, 100);
//3.2 绘制直线(轨迹/绘制路径),还看不见
ctx.lineTo(200, 100);
// 3.3 描边
ctx.stroke();
</scrip>
</body>
</html>
分析:
在Canvas中,我们首先使用document.getElementById()方法来获取canvas对象(这是一个DOM对象),然后使用canvas对象的getContext(‘2d’)方法获取上下文环境对象context,最后再使用context对象的属性和方法来绘制各种图形。下文的代码将只记录最后用context对象的属性和方法绘制图形部分。
Note:以后学习的所有的图形的绘制,我们使用的都是context对象(上下文环境对象)的属性和方法。
第二章 直线图形和线条操作
Canvas坐标系
Canvas使用的坐标系是W3C坐标系:y轴正方向向下。
直线的绘制
一条直线
语法:
ctx.moveTo(x1,y1);//画笔移到起点坐标
ctx.lineTo(x2,y2);//画线至终点坐标
ctx.stroke();//描线,否则无显示
关于线条存在的问题:
- 默认的宽度是1px,但实际看上去是2px
- 默认的颜色是黑色,但实际看上去是灰色
产生原因:
对齐的点是线的中心位置,会把线分成两个0.5px,显示的时候会不饱和增加宽度、稀释颜色。
解决方案:前后移动0.5px。
ctx.moveTo(100, 100.5);
ctx.lineTo(300, 100.5);
ctx.stroke();
两条平行线
语法:
// 画平行线
ctx.moveTo(100, 100);
ctx.lineTo(300, 100);
ctx.moveTo(100, 200);
ctx.lineTo(300, 200);
ctx.stroke();
三条不同颜色不同宽度不同样式的平行折线
- ctx.beginPath(); //用于解决样式覆盖问题
- ctx.lineWidth = 10; //设置线条的宽度 ,默认单位是px,不用加引号,直接写数字
- ctx.strokeStyle() = ‘颜色名’; //设置描边时的颜色
- ctx.lineCap = ‘butt’; //设置线条帽子,有butt、square、round
- ctx.lineJoin = ‘miter’; //设置线条拐点样式,有miter尖的,bevel削平的和round
//开启新路径
ctx.beginPath(); //开启新路径可以解决样式覆盖问题
//蓝色 10px
ctx.moveTo(100, 100);
ctx.lineTo(300, 100);
ctx.strokeStyle = 'blue'; //设置描边时的颜色
ctx.lineWidth = 10; //设置线的宽度,单位默认px
ctx.lineCap = 'butt'; //线条帽子默认样式 butt 没帽子
ctx.lineJoin = 'miter'; //线条拐点样式 miter 默认 尖的
ctx.stroke();
ctx.beginPath(); //开启新路径
//红色 20px
ctx.moveTo(100, 200);
ctx.lineTo(300, 200);
ctx.strokeStyle = 'red'; //设置描边时的颜色
ctx.lineWidth = 20; //设置线的宽度,单位默认px
ctx.lineCap = 'square'; //线条帽子 square 方形帽子,长度变长了
ctx.lineJoin = 'bevel'; //拐点样式 削平的
ctx.stroke();
ctx.beginPath(); //开启新路径
//绿色 30px
ctx.moveTo(100, 300);
ctx.lineTo(300, 300);
ctx.strokeStyle = 'green'; //设置描边时的颜色
ctx.lineWidth = 30; //设置线的宽度,单位默认px
ctx.lineCap = 'round'; //线条帽子 round 圆形帽子
ctx.lineJoin = 'round'; //拐点样式 圆的
ctx.stroke();
绘制虚线
- ctx.setLineDash([]); //设置虚线属性
- ctx.getLineDash(); //获得虚线排列方式
- ctx.lineDashOffset=20; //设置虚线偏移量
// 画虚线
ctx.moveTo(100, 100.5);
ctx.lineTo(300, 100.5);
ctx.setLineDash([5, 10, 15]); //这个数组参数是用来描述虚线的排列方式的
//获取虚线排列方式
console.log(ctx.getLineDash()); //[5, 10, 15, 5, 10, 15]因为实虚交替
//如果是正的值 往后偏移(横左竖下)
//如果是负的值 往前偏移(横右竖上)
ctx.lineDashOffset = 20;
ctx.stroke();
绘制一个填充的三角形
绘制三角形的本质:绘制三条直线首尾相连。
// 1.绘制三角形
ctx.moveTo(100, 100);
ctx.lineTo(200, 100);
ctx.lineTo(200, 200);
//起始点和lineTo的结束点无法完全闭合 缺角
//使用canvas的自动闭合
//ctx.lineTo(100, 100);
ctx.closePath(); //关闭路径 与beginPath()没有一点关系。
ctx.lineWidth = 10;
// 2.描边
//ctx.stroke();
// 3.填充
ctx.fill();
绘制一个中间镂空的正方形
非零填充规则:看一块区域是否填充
- 从这个区域拉一条直线
- 看和这条直线相交的轨迹
- 如果顺时针轨迹 +1
- 如果逆时针轨迹 -1
- 所有轨迹的值计算出来
- 如果计算出来的结果非零,那么填充
- 如果是0,那么不填充
// 1.绘制两个正方形,一大200一小100,套在一起
ctx.moveTo(100, 100);
ctx.lineTo(300, 100);
ctx.lineTo(300, 300);
ctx.lineTo(100, 300);
ctx.closePath();
ctx.moveTo(150, 150);
ctx.lineTo(150, 250);
ctx.lineTo(250, 250);
ctx.lineTo(250, 150);//注意这两个正方形绘制时画笔的方向一顺一逆
ctx.closePath();
//ctx.stroke();
ctx.fillStyle = 'red';
ctx.fill();
绘制一个从黑到白的渐变矩形
思路:面由线构成,线由点构成
// 绘制一个矩形
/* ctx.moveTo(100, 100);
ctx.lineTo(355, 100);
ctx.lineWidth = 30;
//颜色的填充:从黑到白的渐变颜色
ctx.strokeStyle='#fff';*/
// 线是由点构成的
ctx.lineWidth = 30;
for (let i = 0; i < 255; i++) {
ctx.beginPath();
ctx.moveTo(100 + i, 100);
ctx.lineTo(101 + i, 100);
ctx.strokeStyle = 'rgb(' + i + ',' + i + ',' + i + ')';
ctx.stroke();
}
采用面向对象的方式绘制折线图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 1px solid #ccc;
}
</style>
</head>
<body>
<canvas width="600" height="400"></canvas>
<script>
//采用面向对象的方法利用canvas绘制折线图
//1.要有对象,先有构造函数
var LineChart = function(ctx) { //构造函数首字母要大写
// 获取上下文(绘图工具)
this.ctx = ctx || document.querySelector('canvas').getContext('2d');
// 获取画布的大小
this.canvasWidth = this.ctx.canvas.width;
this.canvasHeight = this.ctx.canvas.height;
// 网格的大小
this.gridSize = 10;
// 坐标系的间距
this.space = 20;
// 坐标原点
this.x0 = this.space;
this.y0 = this.canvasHeight - this.space;
// 箭头的大小
this.arrowSize = 10;
// 绘制点的大小
this.dottedSize = 6;
// 点的坐标 和数据有关系 数据可视化
}
//2.定义对象的行为方法(使用原型)
LineChart.prototype.init = function(data) {
this.drawGrid();
this.drawAxis();
this.drawDotted(data);
};
//绘制网格的行为
LineChart.prototype.drawGrid = function() {
//x方向的线
var xLineTotal = Math.floor(this.canvasHeight / this.gridSize);
for (var i = 0; i < xLineTotal; i++) {
this.ctx.beginPath();
this.ctx.moveTo(0, i * this.gridSize - 0.5);
this.ctx.lineTo(this.canvasWidth, i * this.gridSize - 0.5);
this.ctx.strokeStyle = '#ddd';
this.ctx.stroke();
}
//y方向的线
var yLineTotal = Math.floor(this.canvasWidth / this.gridSize);
for (var i = 0; i < yLineTotal; i++) {
this.ctx.beginPath();
this.ctx.moveTo(i * this.gridSize - 0.5, 0);
this.ctx.lineTo(i * this.gridSize - 0.5, this.canvasHeight);
this.ctx.strokeStyle = '#ddd';
this.ctx.stroke();
}
};
//绘制坐标系的行为
LineChart.prototype.drawAxis = function() {
//绘制x轴
this.ctx.beginPath();
this.ctx.moveTo(this.x0, this.y0);
this.ctx.lineTo(this.canvasWidth - this.space, this.y0);
this.ctx.lineTo(this.canvasWidth - this.space - this.arrowSize, this.y0 + this.arrowSize / 2);
this.ctx.lineTo(this.canvasWidth - this.space - this.arrowSize, this.y0 - this.arrowSize / 2);
this.ctx.lineTo(this.canvasWidth - this.space, this.y0)
this.ctx.fill();
this.ctx.strokeStyle = '#000';
this.ctx.stroke();
//绘制y轴
this.ctx.beginPath();
this.ctx.moveTo(this.x0, this.y0);
this.ctx.lineTo(this.space, this.space);
this.ctx.lineTo(this.space + this.arrowSize / 2, this.space + this.arrowSize);
this.ctx.lineTo(this.space - this.arrowSize / 2, this.space + this.arrowSize);
this.ctx.lineTo(this.space, this.space);
this.ctx.fill();
this.ctx.strokeStyle = '#000';
this.ctx.stroke();
};
//绘制点的行为
LineChart.prototype.drawDotted = function(data) {
//1.数据的坐标 需要转换成 canvas坐标
var that = this; //细节(理解一下回调函数和this指向问题)
var prevCanvasX = 0;
var prevCanvasY = 0;
data.forEach(function(item, i) { //i是数据数组的索引号,而item是数组里的每个对象
//canvasX = 原点的坐标 + 数据的坐标
var canvasX = that.x0 + item.x;
//canvasY = 原点的坐标 - 数据的坐标
var canvasY = that.y0 - item.y;
//2.再进行点的绘制
that.ctx.beginPath();
that.ctx.moveTo(canvasX - that.dottedSize / 2, canvasY - that.dottedSize / 2);
that.ctx.lineTo(canvasX + that.dottedSize / 2, canvasY - that.dottedSize / 2);
that.ctx.lineTo(canvasX + that.dottedSize / 2, canvasY + that.dottedSize / 2);
that.ctx.lineTo(canvasX - that.dottedSize / 2, canvasY + that.dottedSize / 2);
that.ctx.closePath();
that.ctx.fill();
//3.把线连起来
//当是第一个点的时候,起点是 x0 y0;
//当不是第一个点的时候,起点是 上一个点;
if (i == 0) {
that.ctx.beginPath();
that.ctx.moveTo(that.x0, that.y0);
that.ctx.lineTo(canvasX, canvasY);
that.ctx.stroke();
} else {
//上一个的
that.ctx.beginPath();
that.ctx.moveTo(prevCanvasX, prevCanvasY);
that.ctx.lineTo(canvasX, canvasY);
that.ctx.stroke();
}
//记录当前的坐标,因为下一次要用
prevCanvasX = canvasX;
prevCanvasY = canvasY;
})
};
//3.初始化
var data = [{
x: 100,
y: 120
}, {
x: 200,
y: 160,
}, {
x: 300,
y: 240
}, {
x: 400,
y: 320
}, {
x: 500,
y: 80
}];
var lineChart = new LineChart();
lineChart.init(data);
</script>
</body>
图形的绘制
矩形
- rect(x,y,w,h) //没有独立路径
- strokeRect(x,y,w,h) //有独立路径,不影响别的绘制
- fillRect(x,y,w,h) //有独立路径,不影响别的绘制
- clearRect(x,y,w,h) //擦除矩形区域
// 绘制矩形的轨迹(不填充或描边就看不见)且不是独立路径
ctx.rect(100, 100, 200, 100);
// 填充或描边
ctx.fillStyle = 'green';
ctx.strokeStyle = 'red'
ctx.stroke();
ctx.fill();
// 绘制矩形,并且有自己的独立路径,相当于开头自带beginPath()
ctx.strokeRect(100, 100, 200, 100);
ctx.fillRect(300, 100, 200, 100);
//清除矩形的内容,擦除矩形部分区域
ctx.clearRect(200, 110, 200, 80);
用渐变方案绘制一个渐变的矩形
//fillStyle 'pink' '#000' 'rgb()' 'rgba()'
//也可以使用一个渐变的方案来填充矩形
//canvas中创建一个渐变方案
//渐变是有长度的(x0,y0,x1,y1)x0,y0是起始点,x1y1是结束点
var linearGradient = ctx.createLinearGradient(100, 100, 500, 100);
linearGradient.addColorStop(0, 'pink'); //0是指在百分之零的位置
linearGradient.addColorStop(0.5, 'red'); //0.5是指在百分之五十的位置
linearGradient.addColorStop(1, 'blue'); //1是指在百分之百的位置
//ctx.fillStyle = 'pink';以前是放颜色现在是放渐变方案
ctx.fillStyle = linearGradient;
ctx.fillRect(100, 100, 400, 100);
//pink----->blue
//回想线性渐变------>要素:方向 起始颜色 结束颜色
//通过两个点的坐标可以控制 渐变方向
绘制正多边形
1.封装成函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 1px solid #ccc;
}
</style>
</head>
<body>
<canvas width="600" height="400"></canvas>
<script>
var myCanvas = document.querySelector('canvas');
var ctx = myCanvas.getContext('2d');
function creatPolygon(ctx, n, dx, dy, size) {
ctx.beginPath();
var degree = (2 * Math.PI) / n;
for (var i = 0; i < n; i++) {
var x = Math.cos(i * degree);
var y = Math.sin(i * degree);
//ctx.moveTo(0, 0);
ctx.lineTo(x * size + dx, y * size + dy);
}
ctx.closePath();
}
creatPolygon(ctx, 36, 100, 75, 50);
ctx.fillStyle = 'hotpink';
ctx.fill();
</script>
</body>
</html>
2.封装成对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
canvas {
border: 1px solid #ccc;
}
</style>
</head>
<body>
<canvas width="600" height="400"></canvas>
<script>
//采用面向对象的方法利用canvas绘制正多边形
//1.要有对象,先有构造函数
var CreatePolygon = function(n, dx, dy, size, color, ctx) {
// 获取上下文(绘图工具)
this.ctx = ctx || document.querySelector('canvas').getContext('2d');
// 获取多边形边数
this.n = n;
// 获取多边形中心位置
this.dx = dx;
this.dy = dy;
// 获取多边形顶点距离多边形中心长度大小
this.size = size;
// 获取多边形填充的颜色
this.color = color || 'black';
}
//2.设置对象的行为,绘制一个正多边形
CreatePolygon.prototype.fill = function() {
this.ctx.beginPath();
var degree = (2 * Math.PI) / this.n;
for (var i = 0; i < this.n; i++) {
var x = Math.cos(i * degree);
var y = Math.sin(i * degree);
//ctx.moveTo(0, 0);
this.ctx.lineTo(x * this.size + this.dx, y * this.size + this.dy);
}
this.ctx.closePath();
this.ctx.fillStyle = this.color;
this.ctx.fill();
};
CreatePolygon.prototype.stroke = function() {
this.ctx.beginPath();
var degree = (2 * Math.PI) / this.n;
for (var i = 0; i < this.n; i++) {
var x = Math.cos(i * degree);
var y = Math.sin(i * degree);
//ctx.moveTo(0, 0);
this.ctx.lineTo(x * this.size + this.dx, y * this.size + this.dy);
}
this.ctx.closePath();
this.ctx.strokeStyle = this.color;
this.ctx.stroke();
};
//3.初始化
var createPolygon = new CreatePolygon(6, 200, 200, 70, 'yellow');
createPolygon.stroke();
createPolygon.fill();
</script>
</body>