目录
一、初识canvas
Canvas是H5新增的一个标签, 我们可以通过JS在这个标签上绘制各种图案
Canvas 拥有多种绘制路径、矩形、圆形、字符以及图片的方法
Canvas可以替换像flash等其他做动画或游戏的插件的一个标签,能够大大减小页面的结构大小。让页面加载速度变快。
1.1 基本使用
- canvas标签可以理解为一个‘div’
只不过div是存放元素的,而canvas则是提供了一个区域用来绘制图形。
- canvas标签习惯上在标签内部对其大小进行设置,例如:
<canvas width=’300’ height=’300’>您的浏览器不支持canvas标签,请变更支持cnavas标签的浏览器</canvas>
- canvas标签本身就是一个普通的画板,除此之外没有任何多余的功能
内部的所有内容或图形都需要通过js脚本来完成。
首先,创建canvas画布,在body标签中创建一个canvas标签
其次,进行相关样式设置
最后,如果想要在画布上进行操作,则需要通过脚本来创建一个‘画家’,来帮助我们进行内容绘制。
1.2 示例
*{
margin: 0;
padding: 0;
}
canvas{
background: red;
}
<canvas>您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas>
// 2.通过js代码拿到canvas标签
let oCanvas = document.querySelector("canvas");
// 3.从canvas标签中获取到绘图工具
let oCtx = oCanvas.getContext("2d");
// 4.通过绘图工具在canvas标签上绘制图形
// 4.1设置路径的起点
oCtx.moveTo(50, 50);
// 4.2设置路径的终点
oCtx.lineTo(200, 50);
// 4.3告诉canvas将这些点连接起来
oCtx.stroke();
1.1 注意点
- canvas标签有默认的宽度和高度:默认的宽度是300px;默认的高度是150px
-
不能通过CSS设置画布的宽高:通过CSS设置画布宽高会在默认宽高的基础上拉伸,如果需要设置canvas宽高请通过元素行内属性width和height设置
<canvas width="500" height="500"></canvas>
- 线条默认宽度和颜色:通过canvas绘制的线条默认宽度是1px, 颜色是纯黑色;但是由于默认情况下canvas会将线条的中心点和像素的底部对齐,所以会导致显示效果是2px和非纯黑色问题
二、canvas线条相关属性
2.1 常用属性
- 线型宽度:ctx.lineWith=数值;
- 设置端点:ctx.lineCap='端点类型';
butt 无端点 round圆角端点 square方块端点
- 设置连接:ctx.lineJoin='连接类型';
round圆弧连接 bevel截面连接 miter直角连接
- 设置虚线:ctx.setLineDash([第一段长度,第二段长度...]);
*{
margin: 0;
padding: 0;
}
canvas{
background: red;
}
<canvas width="300" height="200">您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas>
2.2 示例
示例1:
//拿到画布
let oCanvas = document.querySelector("canvas");
//拿到绘制工具
let ctx = oCanvas.getContext("2d");
//修改线条高度
ctx.lineWidth = 10;
//修改线条颜色
ctx.strokeStyle = "#fff";
//修改线条两端样式
// ctx.lineCap="";
// ctx.lineCap="square";
ctx.lineCap="round";
//设置虚线
ctx.setLineDash([10,5,20]);
//利用后置工具绘制直线
// 设置起始位置
ctx.moveTo(50,50.5);
// 设置路径上的点
ctx.lineTo(200,50.5);
ctx.lineTo(200,150.5);
ctx.lineTo(50,150.5);
ctx.lineTo(50,50.5);
// 绘制已定义的路径
ctx.stroke();
示例2:
var huaban = document.querySelector('canvas');
var ctx = huaban.getContext('2d');
ctx.beginPath(); //开始
ctx.moveTo(40, 80); //起始位置
ctx.lineTo(230, 80);
ctx.lineTo(230, 180);
ctx.lineWidth = 5; //线型宽度
ctx.lineCap = 'butt'; //端点类型 无端点
ctx.lineJoin = 'miter'; //设置连接 直角连接
ctx.setLineDash([10, 5, 20]); //设置虚线 第一段长度,第二段长度,第三段长度...
ctx.stroke();
ctx.closePath(); //结束
2.3 绘制多根线条
a.多根线条注意点:
- 如果是同一个路径, 那么路径样式会被重用(第二次绘制会复用第一次的样式)
- 如果是同一个路径, 那么后设置的路径样式会覆盖先设置的路径样式
b.如何给每根线条单独设置路径样式?
每根线条都开启一个新的路径即可
c.示例:
//拿到画布
let oCanvas = document.querySelector("canvas");
//拿到绘制工具
let ctx = oCanvas.getContext("2d");
// //修改线条两端样式
// // ctx.lineCap="";
// // ctx.lineCap="square";
// ctx.lineCap="round";
//设置虚线
// ctx.setLineDash([10,5,20]);
//利用后置工具绘制直线
// 设置起始位置
ctx.moveTo(50, 50);
// 设置路径上的点
ctx.lineTo(200, 50);
// 修改线条高度
ctx.lineWidth = 20;
//修改线条颜色
ctx.strokeStyle = "blue";
// 绘制已定义的路径
ctx.stroke();
ctx.beginPath(); // 重新开启一个路径
ctx.moveTo(50, 100);
ctx.lineTo(200, 100);
ctx.lineWidth = 10; // 重新设置当前路径样式
ctx.strokeStyle = "white";
ctx.stroke();
ctx.beginPath(); // 重新开启一个路径
ctx.moveTo(50, 150);
ctx.lineTo(200, 150);
ctx.lineWidth = 15; // 重新设置当前路径样式
ctx.strokeStyle = "green";
ctx.stroke();
三、canvas绘制简单图形
3.1 绘制三角形
closePath():自动创建从当前点回到起始点的路径
lineJoin():设置相交线的拐点样式 miter(默认)、round、bevel
如果通过lineTo()来闭合图形, 那么是不能很好的闭合
默认情况下不会自动从最后一个点连接到起点
* {
margin: 0;
padding: 0;
}
canvas {
background: red;
display: block;
margin: 0 auto;
}
<canvas width="300" height="240"
>您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas
>
//拿到画布
let oCanvas = document.querySelector("canvas");
//拿到绘制工具
let ctx = oCanvas.getContext("2d");
ctx.moveTo(50,50);
ctx.lineTo(200,50);
ctx.lineTo(200,200);
// ctx.lineTo(50,50);
ctx.closePath(); // 自动创建从当前点回到起始点的路径
ctx.lineWidth = 10;
// 设置相交线的拐点样式 miter(默认)、round、bevel
ctx.lineJoin = "round";
ctx.stroke();
3.2 路径绘制
- 设置笔触(落笔)点坐标:ctx.moveTo(横,纵);
- 设置笔触路径:ctx.lineTo(横,纵);
- 设置笔触颜色:ctx.strokeStyle='颜色';
- 笔触的绘制:ctx.stroke();
- 设置填充颜色:ctx.fillStyle='颜色';
- 设置封闭图形填充:ctx.fill();
- 开启路径绘制:ctx.beignPath()
- 结束路径绘制:ctx.closePath()
<canvas width="300" height="300">您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas>
canvas {
border: 1px solid;
}
<script>
var huaban = document.querySelector('canvas');
var ctx = huaban.getContext('2d');
//保证有效的图形能够独立
ctx.beginPath(); //开启路径绘制
ctx.moveTo(50, 50); //设置笔触(落笔)点坐标
ctx.lineTo(100, 50); //设置笔触路径
ctx.lineTo(75, 100); //设置笔触路径
ctx.lineTo(50, 50); //设置笔触路径
ctx.fillStyle = 'orange';
ctx.fill(); //设置封闭图形填充
ctx.closePath(); //结束路径绘制
ctx.beginPath(); //开启路径绘制
ctx.moveTo(60, 120); //设置笔触(落笔)点坐标
ctx.lineTo(150, 190); //设置笔触路径
ctx.lineTo(230, 190); //设置笔触路径
ctx.lineTo(60, 120); //设置笔触路径
ctx.fillStyle = 'blue';
ctx.fill(); //设置封闭图形填充
ctx.closePath(); //结束路径绘制
</script>
3.3 矩形绘制
* {
margin: 0;
padding: 0;
}
canvas {
background: red;
display: block;
margin: 0 auto;
}
<canvas width="500" height="500"></canvas>
let oCanvas = document.querySelector("canvas");
let oCtx = oCanvas.getContext("2d");
oCtx.moveTo(100, 100);
oCtx.lineTo(300, 100);
oCtx.lineTo(300, 300);
oCtx.lineTo(100, 300);
oCtx.closePath();
oCtx.moveTo(250, 150);
oCtx.lineTo(150, 150);
oCtx.lineTo(150, 250);
oCtx.lineTo(250, 250);
oCtx.closePath();
/*
注意点: 只要没有手动开启新的路径, 那么使用的都是默认路径
如果都是默认路径, 那么设置的样式在同一个路径中都是有效的
* */
oCtx.fill();
/*
对于同一路径,在填充的时候会遵循非零环绕规则
从当前的区域拉出一条直线, 遇到顺时针相交的线就+1, 遇到逆时针相交的线就-1
最终计算的结果如果是0就不填充, 如果不是0就填充
* */
绘制填充矩形:ctx.fillRect(横,纵,宽,高);
绘制描边矩形:ctx.strokeRect(横,纵,宽,高);
擦除矩形区域:ctx.clearRect(横,纵,宽,高);
ctx.fillStyle='blue';
ctx.fillRect(50,70,50,50);
ctx.strokeRect(150,70,50,50);
ctx.clearRect(75,70,100,50);
3.4 绘制虚线
setLineDash():设置
lineDashOffset():设置偏移位
//拿到画布
let oCanvas = document.querySelector("canvas");
//拿到绘制工具
let ctx = oCanvas.getContext("2d");
ctx.moveTo(20,120);
ctx.lineTo(280,120);
ctx.lineWidth = 2;
ctx.strokeStyle = "white";
// ctx.setLineDash([2,8]);
ctx.setLineDash([2,8,10]);
// console.log(ctx.getLineDash());
//设置虚线的偏移位
ctx.lineDashOffset = -200;
ctx.stroke();
3.5 绘制表格
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
<canvas width="500" height="400">您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas>
//拿到画布
let oCanvas = document.querySelector("canvas");
//拿到绘制工具
let ctx = oCanvas.getContext("2d");
//定义变量,保存小方格的尺寸
let gridSize = 10;
//拿到 canvas 的宽高
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
// 计算在垂直和水平方向可以画多少条横线
let row = Math.floor(canvasHeight / gridSize);
let col = Math.floor(canvasWidth / gridSize);
//绘制垂直方向的横线
for (let i = 0; i < row; i++) {
ctx.beginPath();
ctx.moveTo(0, i * gridSize - 0.5); //设置路径的起点
ctx.lineTo(canvasWidth, i * gridSize - 0.5); //设置路径的终点
ctx.stroke();
}
//绘制水平方向的横线
for (let i = 0; i < col; i++) {
ctx.beginPath();
ctx.moveTo(i * gridSize - 0.5, 0); //设置路径的起点
ctx.lineTo(i * gridSize - 0.5, canvasHeight); //设置路径的终点
ctx.stroke();
}
3.6 绘制坐标系
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
<canvas width="500" height="400">您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas>
//拿到画布
let oCanvas = document.querySelector("canvas");
//拿到绘制工具
let ctx = oCanvas.getContext("2d");
//定义变量,保存小方格的尺寸
let gridSize = 50;
//拿到 canvas 的宽高
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
// 计算在垂直和水平方向可以画多少条横线
let row = Math.floor(canvasHeight / gridSize);
let col = Math.floor(canvasWidth / gridSize);
//绘制垂直方向的横线
for (let i = 0; i < row; i++) {
ctx.beginPath();
ctx.moveTo(0, i * gridSize - 0.5); //设置路径的起点
ctx.lineTo(canvasWidth, i * gridSize - 0.5); //设置路径的终点
ctx.strokeStyle = "#ccc";
ctx.stroke();
}
//绘制水平方向的横线
for (let i = 0; i < col; i++) {
ctx.beginPath();
ctx.moveTo(i * gridSize - 0.5, 0); //设置路径的起点
ctx.lineTo(i * gridSize - 0.5, canvasHeight); //设置路径的终点
ctx.strokeStyle = "#ccc";
ctx.stroke();
}
//计算坐标系原点的位置
let originX = gridSize;
let originY = canvasHeight - gridSize;
//计算X轴终点位置
let endX = canvasWidth - gridSize;
//绘制X轴
ctx.beginPath();
ctx.moveTo(originX,originY);
ctx.lineTo(endX,originY);
ctx.strokeStyle = "#f40";
ctx.stroke();
//绘制X轴的箭头
ctx.lineTo(endX -10,originY + 5);
ctx.lineTo(endX -10,originY - 5);
ctx.lineTo(endX,originY);
ctx.fill();
//计算Y轴终点位置
let endY = gridSize;
//绘制Y轴
ctx.beginPath();
ctx.moveTo(originX,originY);
ctx.lineTo(originX,endY);
ctx.strokeStyle = "#f40";
ctx.stroke();
//绘制Y轴的箭头
ctx.lineTo(originX -5,endY + 10);
ctx.lineTo(originX +5,endY + 10);
ctx.lineTo(originX,endY);
ctx.fill();
3.7 绘制数据点
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
<canvas width="500" height="400">您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas>
//拿到画布
let oCanvas = document.querySelector("canvas");
//拿到绘制工具
let ctx = oCanvas.getContext("2d");
//定义变量,保存小方格的尺寸
let gridSize = 50;
//拿到 canvas 的宽高
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
// 计算在垂直和水平方向可以画多少条横线
let row = Math.floor(canvasHeight / gridSize);
let col = Math.floor(canvasWidth / gridSize);
//绘制垂直方向的横线
for (let i = 0; i < row; i++) {
ctx.beginPath();
ctx.moveTo(0, i * gridSize - 0.5); //设置路径的起点
ctx.lineTo(canvasWidth, i * gridSize - 0.5); //设置路径的终点
ctx.strokeStyle = "#ccc";
ctx.stroke();
}
//绘制水平方向的横线
for (let i = 0; i < col; i++) {
ctx.beginPath();
ctx.moveTo(i * gridSize - 0.5, 0); //设置路径的起点
ctx.lineTo(i * gridSize - 0.5, canvasHeight); //设置路径的终点
ctx.strokeStyle = "#ccc";
ctx.stroke();
}
//计算坐标系原点的位置
let originX = gridSize;
let originY = canvasHeight - gridSize;
//计算X轴终点位置
let endX = canvasWidth - gridSize;
//绘制X轴
ctx.beginPath();
ctx.moveTo(originX, originY);
ctx.lineTo(endX, originY);
ctx.strokeStyle = "#f40";
ctx.stroke();
//绘制X轴的箭头
ctx.lineTo(endX - 10, originY + 5);
ctx.lineTo(endX - 10, originY - 5);
ctx.lineTo(endX, originY);
ctx.fill();
//计算Y轴终点位置
let endY = gridSize;
//绘制Y轴
ctx.beginPath();
ctx.moveTo(originX, originY);
ctx.lineTo(originX, endY);
ctx.strokeStyle = "#f40";
ctx.stroke();
//绘制Y轴的箭头
ctx.lineTo(originX - 5, endY + 10);
ctx.lineTo(originX + 5, endY + 10);
ctx.lineTo(originX, endY);
ctx.fill();
// 拿到服务器返回数据
let list = [
{
x: 100,
y: 300,
},
{
x: 200,
y: 200,
},
{
x: 300,
y: 250,
},
{
x: 400,
y: 100,
},
];
let dotLocation = {
x: 100,
y: 300,
};
let dotSize = 20;
for (let i = 0; i < list.length; i++) {
ctx.beginPath();
ctx.moveTo(list[i].x - dotSize / 2, list[i].y - dotSize / 2);
ctx.lineTo(list[i].x + dotSize - dotSize / 2, list[i].y - dotSize / 2);
ctx.lineTo(
list[i].x + dotSize - dotSize / 2,
list[i].y + dotSize - dotSize / 2
);
ctx.lineTo(list[i].x - dotSize / 2, list[i].y + dotSize - dotSize / 2);
ctx.closePath();
ctx.fill();
}
3.8 绘制折线图
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
class LineChart {
constructor(width = 300, height = 150) {
// 1.创建canvas
this.canvas = document.createElement("canvas");
this.canvas.width = width;
this.canvas.height = height;
document.body.appendChild(this.canvas);
// 2.拿到绘图工具
this.ctx = this.canvas.getContext("2d");
}
//画小格子
drawGrid(gridSize = 20) {
let ctx = this.ctx;
//拿到 canvas 的宽高
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
// 计算在垂直和水平方向可以画多少条横线
let row = Math.floor(canvasHeight / gridSize);
let col = Math.floor(canvasWidth / gridSize);
//绘制垂直方向的横线
for (let i = 0; i < row; i++) {
ctx.beginPath();
ctx.moveTo(0, i * gridSize - 0.5); //设置路径的起点
ctx.lineTo(canvasWidth, i * gridSize - 0.5); //设置路径的终点
ctx.strokeStyle = "#ccc";
ctx.stroke();
}
//绘制水平方向的横线
for (let i = 0; i < col; i++) {
ctx.beginPath();
ctx.moveTo(i * gridSize - 0.5, 0); //设置路径的起点
ctx.lineTo(i * gridSize - 0.5, canvasHeight); //设置路径的终点
ctx.strokeStyle = "#ccc";
ctx.stroke();
}
}
//画坐标系
drawCoor(gridSize = 20) {
let ctx = this.ctx;
let canvasWidth = this.ctx.canvas.width;
let canvasHeight = this.ctx.canvas.height;
//计算坐标系原点的位置
let originX = gridSize;
let originY = canvasHeight - gridSize;
//计算X轴终点位置
let endX = canvasWidth - gridSize;
//绘制X轴
ctx.beginPath();
ctx.moveTo(originX, originY);
ctx.lineTo(endX, originY);
ctx.strokeStyle = "#f40";
ctx.stroke();
//绘制X轴的箭头
ctx.lineTo(endX - 10, originY + 5);
ctx.lineTo(endX - 10, originY - 5);
ctx.lineTo(endX, originY);
ctx.fill();
//计算Y轴终点位置
let endY = gridSize;
//绘制Y轴
ctx.beginPath();
ctx.moveTo(originX, originY);
ctx.lineTo(originX, endY);
ctx.strokeStyle = "#f40";
ctx.stroke();
//绘制Y轴的箭头
ctx.lineTo(originX - 5, endY + 10);
ctx.lineTo(originX + 5, endY + 10);
ctx.lineTo(originX, endY);
ctx.fill();
}
//绘制数据点
drawDot(list, dotSize = 10) {
let ctx = this.ctx;
for (let i = 0; i < list.length; i++) {
ctx.beginPath();
ctx.moveTo(list[i].x - dotSize / 2, list[i].y - dotSize / 2);
ctx.lineTo(
list[i].x + dotSize - dotSize / 2,
list[i].y - dotSize / 2
);
ctx.lineTo(
list[i].x + dotSize - dotSize / 2,
list[i].y + dotSize - dotSize / 2
);
ctx.lineTo(
list[i].x - dotSize / 2,
list[i].y + dotSize - dotSize / 2
);
ctx.closePath();
ctx.fill();
}
}
//绘制折线
drawLine(list) {
let ctx = this.ctx;
ctx.beginPath();
for (let i = 0; i < list.length; i++) {
if (i === 0) {
ctx.moveTo(list[i].x, list[i].y);
} else {
ctx.lineTo(list[i].x, list[i].y);
}
}
ctx.stroke();
}
}
// 拿到服务器返回数据
let list = [
{
x: 100,
y: 300,
},
{
x: 200,
y: 200,
},
{
x: 300,
y: 250,
},
{
x: 400,
y: 100,
},
];
let dotLocation = {
x: 100,
y: 300,
};
let lineChart = new LineChart(500, 400);
lineChart.drawGrid(50);
lineChart.drawCoor(50);
lineChart.drawDot(list, 20);
lineChart.drawLine(list);
3.9 绘制柱状图
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
class LineChart {
constructor(width = 300, height = 150) {
// 1.创建canvas
this.canvas = document.createElement("canvas");
this.canvas.width = width;
this.canvas.height = height;
document.body.appendChild(this.canvas);
// 2.拿到绘图工具
this.ctx = this.canvas.getContext("2d");
}
//画小格子
drawGrid(gridSize = 20) {
let ctx = this.ctx;
//拿到 canvas 的宽高
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
// 计算在垂直和水平方向可以画多少条横线
let row = Math.floor(canvasHeight / gridSize);
let col = Math.floor(canvasWidth / gridSize);
//绘制垂直方向的横线
for (let i = 0; i < row; i++) {
ctx.beginPath();
ctx.moveTo(0, i * gridSize - 0.5); //设置路径的起点
ctx.lineTo(canvasWidth, i * gridSize - 0.5); //设置路径的终点
ctx.strokeStyle = "#ccc";
ctx.stroke();
}
//绘制水平方向的横线
for (let i = 0; i < col; i++) {
ctx.beginPath();
ctx.moveTo(i * gridSize - 0.5, 0); //设置路径的起点
ctx.lineTo(i * gridSize - 0.5, canvasHeight); //设置路径的终点
ctx.strokeStyle = "#ccc";
ctx.stroke();
}
}
//画坐标系
drawCoor(gridSize = 20) {
let ctx = this.ctx;
let canvasWidth = this.ctx.canvas.width;
let canvasHeight = this.ctx.canvas.height;
//计算坐标系原点的位置
let originX = gridSize;
let originY = canvasHeight - gridSize;
//计算X轴终点位置
let endX = canvasWidth - gridSize;
//绘制X轴
ctx.beginPath();
ctx.moveTo(originX, originY);
ctx.lineTo(endX, originY);
ctx.strokeStyle = "#f40";
ctx.stroke();
//绘制X轴的箭头
ctx.lineTo(endX - 10, originY + 5);
ctx.lineTo(endX - 10, originY - 5);
ctx.lineTo(endX, originY);
ctx.fill();
//计算Y轴终点位置
let endY = gridSize;
//绘制Y轴
ctx.beginPath();
ctx.moveTo(originX, originY);
ctx.lineTo(originX, endY);
ctx.strokeStyle = "#f40";
ctx.stroke();
//绘制Y轴的箭头
ctx.lineTo(originX - 5, endY + 10);
ctx.lineTo(originX + 5, endY + 10);
ctx.lineTo(originX, endY);
ctx.fill();
}
//绘制矩形
drawRectangle(list,gridSize=20) {
let ctx = this.ctx;
let canvasHeight = this.ctx.canvas.height;
let originY = canvasHeight - gridSize;
//绘制矩形
for (let i = 0; i < list.length; i++) {
let barHeight = originY - list[i].y;
ctx.fillRect(list[i].x, list[i].y, gridSize, barHeight);
}
}
}
// 拿到服务器返回数据
let list = [
{
x: 100,
y: 300,
},
{
x: 200,
y: 200,
},
{
x: 300,
y: 250,
},
];
let dotLocation = {
x: 100,
y: 300,
};
let lineChart = new LineChart(500, 400);
lineChart.drawGrid(50);
lineChart.drawCoor(50);
lineChart.drawRectangle(list,50);
四、渐变背景颜色
和普通标签一样我们也可以给填充的图形设置线性渐变和径向渐变的背景颜色
4.1 设置图形渐变背景颜色步骤
- 通过绘图工具创建渐变背景颜色
- 指定渐变的范围
- 将渐变背景颜色设置给对应的图形
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
<canvas width="500" height="400"
>您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas
>
let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
/* 1.创建渐变方案
可以通过x0,y0 / x1,y1确定渐变的方向和渐变的范围
* */
let linearGradient = ctx.createLinearGradient(100, 100, 300, 300);
// ctx.createRadialGradient();//径向渐变
/*2.指定渐变的范围
第一个参数是一个百分比 0~1
第二个参数是一个颜色
* */
linearGradient.addColorStop(0, "green");
linearGradient.addColorStop(0.5, "yellow");
linearGradient.addColorStop(1, "blue");
/*3.将渐变背景颜色设置给对应的图形
**/
ctx.fillStyle = linearGradient;
ctx.fillRect(100, 100, 200, 200);
4.2 绘制圆弧
a.基本概念(请翻开初中数学课本) 哈哈~
角度: 一个圆360度, 一个半圆是180度
弧度: 一个圆2π, 一个半圆π
b.角度转换弧度公式:
∵ 180角度 = π弧度
∴ 1角度 = π/180;
∴ 弧度 = 角度 * π/180;
90角度 * π/180 = π/2
c.弧度转换角度公式:
∵ π弧度 = 180角度
∴ 1弧度 = 180/π
∴ 角度 = 弧度 * 180/π
π/2 * 180/π = 180/2 = 90度
ctx.arc(圆心x,圆心y,半径r,开始弧度,结束弧度,false);
ctx.arc(圆心x,圆心y,半径r,开始弧度,结束弧度,true);
false就是顺时针绘制, true就是逆时针绘制
false先开始后结束, true是先结束后开始,都是顺时针方向画弧
示例1:画圆和半圆
canvas {
border: 1px solid;
}
<canvas width="300" height="300">您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas>
var huaban = document.querySelector('canvas');
var ctx = huaban.getContext('2d');
ctx.beginPath(); //开始落脚
//ctx.arc(150, 150, 50, 0 * Math.PI, 1 * Math.PI, false);//半圆
ctx.arc(150, 150, 50, 0 * Math.PI, 2 * Math.PI, false);//圆
// ctx.stroke();
ctx.fillStyle = 'skyblue'; //填充
ctx.fill();
ctx.closePath(); //结束
示例1:绘制扇形
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
<canvas width="500" height="400"></canvas>
// 1.拿到canvas
let canvas = document.querySelector("canvas");
// 2.从canvas中拿到绘图工具
let ctx = canvas.getContext("2d");
//设置起点
ctx.moveTo(100,100);
//ctx.arc(圆心x,圆心y,半径r,开始弧度,结束弧度,false);
ctx.arc(100,100,100,0,Math.PI/2);
ctx.closePath();
// ctx.stroke();
ctx.fill();
4.3 绘制饼状图
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
<canvas width="500" height="400"></canvas>
// 1.拿到canvas
let canvas = document.querySelector("canvas");
// 2.从canvas中拿到绘图工具
let ctx = canvas.getContext("2d");
//计算圆心的位置
let rx = ctx.canvas.width / 2;
let ry = ctx.canvas.height / 2;
/*
//绘制第一个扇形
//ctx.arc(圆心x,圆心y,半径r,开始弧度,结束弧度,false);
ctx.moveTo(rx, ry);
ctx.arc(rx, ry, 100, 0, Math.PI / 2);
ctx.fillStyle = randomColor();
ctx.fill();
//绘制第二个扇形
//ctx.arc(圆心x,圆心y,半径r,开始弧度,结束弧度,false);
ctx.beginPath(); // 开启新路径
ctx.moveTo(rx, ry);
ctx.arc(rx, ry, 100, Math.PI / 2, Math.PI);
ctx.fillStyle = randomColor();
ctx.fill();
//绘制第三个扇形
//ctx.arc(圆心x,圆心y,半径r,开始弧度,结束弧度,false);
ctx.beginPath(); // 开启新路径
ctx.moveTo(rx, ry);
ctx.arc(rx, ry, 100, Math.PI, Math.PI + Math.PI / 2);
ctx.fillStyle = randomColor();
ctx.fill();
//绘制第四个扇形
//ctx.arc(圆心x,圆心y,半径r,开始弧度,结束弧度,false);
ctx.beginPath(); // 开启新路径
ctx.moveTo(rx, ry);
ctx.arc(rx, ry, 100, Math.PI + Math.PI / 2, Math.PI * 2);
ctx.fillStyle = randomColor();
ctx.fill();
*/
let startAngle = 0;//开始的弧度
for (let i = 1; i <= 4; i++) {
// 结束的弧度
let endAngle = i * Math.PI / 2;
//ctx.arc(圆心x,圆心y,半径r,开始弧度,结束弧度,false);
ctx.beginPath(); // 开启新路径
ctx.moveTo(rx, ry);
ctx.arc(rx, ry, 100, startAngle, endAngle);
ctx.fillStyle = randomColor();
ctx.fill();
// 上一次结束的角度是下一次的起始角度
startAngle = endAngle;
}
//随机生成圆弧填充颜色
function randomColor() {
let r = Math.floor(Math.random() * 256);
let g = Math.floor(Math.random() * 256);
let b = Math.floor(Math.random() * 256);
return `rgb(${r},${g},${b})`;
}
4.4 绘制文字
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
<canvas width="500" height="400"></canvas>
// 1.拿到canvas
let canvas = document.querySelector("canvas");
// 2.从canvas中拿到绘图工具
let ctx = canvas.getContext("2d");
//3.绘制参考线
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
ctx.moveTo(0,canvasHeight / 2);
ctx.lineTo(canvasWidth,canvasHeight / 2);
ctx.stroke();
ctx.moveTo(canvasWidth / 2,0);
ctx.lineTo(canvasWidth / 2,canvasHeight);
ctx.stroke();
//4.绘制文字
let str = '世界很大';
//通过 font 属性可以设置文字的大小和样式
ctx.font = "50px 微软雅黑";
//textBaseline:修改文字垂直方向的对齐方式
// 在对齐的时候是以绘制文字的y作为参考点进行对齐的
//textBaseline = top/middle/bottom
ctx.textBaseline = "middle";
//textAlign:修改文字水平方向对齐方式
// 在对齐的时候是以文字的x作为参考点进行的
//textAlign = star/end/center
ctx.textAlign = "end";
/**
* ctx.strokeText(需要绘制的文字,绘制的位置);
* 在绘制文字的时候是以文字的左下角作为参考点进行绘制
*/
// ctx.strokeText(str,canvasWidth / 2,canvasHeight / 2); 或
// fillText()绘制的文字是实心;strokeText()绘制的文字是空心
ctx.fillText(str,canvasWidth / 2,canvasHeight / 2);
4.5 图片绘制
描述:canvas中绘制图片必须等到图片完全加载成功之后再上屏,否则就会绘制失败。
方法:
ctx.drawImage();
语法:
- ctx.drawImage(img对象,x,y)
如果只有三个参数, 那么第一个参数就是需要绘制的图片,后面的两个参数是指定图片从什么位置开始绘制
oCtx.drawImage(oImg, 100, 100);
- ctx.drawImage(img对象,x,y,w,h)
如果只有五个参数, 那么第一个参数就是需要绘制的图片,第二和第三个参数是指定图片从什么位置开始绘制,第四、第五两个参数是指定图片需要拉伸到多大
oCtx.drawImage(oImg, 100, 100, 100, 100);
- ctx.drawImage(img对象,裁切x,裁切y,裁切w,裁切h,绘制x,绘制y,绘制w,绘制h)
如果有九个参数, 那么第一个参数就是需要绘制的图片,第2~3个参数指定图片上定位的位置,第4~5个参数指定从定位的位置开始截取多大的图片,第6~7个参数指定图片从什么位置开始绘制,最后的两个参数是指定图片需要拉伸到多大
示例:
canvas {
border: 1px solid;
}
<canvas width="1300" height="600">您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas>
var huaban = document.querySelector('canvas');
var ctx = huaban.getContext('2d');
ctx.beginPath(); //开始
//创建图片
var tempImage = new Image();
//加载图片
tempImage.src = 'img/timg.jpg';
//图片完全加载后再绘制
tempImage.onload = function() {
//将加载的图片绘制到canvas上
ctx.drawImage(tempImage, 190, 65, 210, 80, 50, 50, 80, 100);
};
ctx.closePath(); //结束
五、绘制动画
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
<canvas width="500" height="400"></canvas>
// 1.拿到canvas
let canvas = document.querySelector("canvas");
// 2.从canvas中拿到绘图工具
let ctx = canvas.getContext("2d");
//3.加载图片
let oImg = new Image();
oImg.onload = function (){
//3.1计算每一张图片的宽高
let imageWidth = oImg.width;
let imageHeight = oImg.height;
let personWidth = imageWidth / 4;
let personHeight = imageHeight / 4;
//3.2计算绘制的位置
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
let originX = canvasWidth / 2 - personWidth / 2;
let originY = canvasHeight / 2 - personHeight / 2;
//3.3绘制图片
ctx.drawImage(oImg,0,personHeight *2,personWidth,personHeight,originX,originY,personWidth,personHeight);
//3.4实现逐帧动画
let index = 1;
setInterval(function(){
// 清除以前绘制的图片
ctx.clearRect(0,0,canvasWidth,canvasHeight);
//重新绘制
ctx.drawImage(oImg,index * personWidth,personHeight *2,personWidth,personHeight,originX,originY,personWidth,personHeight);
index++;
if(index > 3){
index = 0;
}
},50);
};
oImg.src = "images/person.png";
对上面的代码进行封装:
// 1.拿到canvas
let canvas = document.querySelector("canvas");
// 2.从canvas中拿到绘图工具
let ctx = canvas.getContext("2d");
/*
面向对象思想:
找到小人对象, 你给我画到指定的位置, 你给我走起来, 你给我停下来
* */
class Person {
constructor(canvas, x, y) {
this.canvas = canvas;
this.x = x;
this.y = y;
this.ctx = this.canvas.getContext("2d");
this.index = 1;
this.timeId = null;
// 0正面/1左面/2右面/3后面
this.direction = 0;
}
//绘制
render() {
//1.加载图片
let oImg = new Image();
oImg.onload = () => {
//1.1计算每一张图片的宽高
let imageWidth = oImg.width;
let imageHeight = oImg.height;
let personWidth = imageWidth / 4;
let personHeight = imageHeight / 4;
//1.2绘制图片
this.ctx.drawImage(
oImg, // 需要绘制的图片
0, //图片定位的位置
this.direction * personHeight, //图片定位的位置
personWidth, // 图片裁剪的大小
personHeight, // 图片裁剪的大小
this.x, // 图片绘制的位置
this.y, // 图片绘制的位置
personWidth, // 指定图片绘制大小
personHeight // 指定图片绘制大小
);
this.oImg = oImg;
this.personWidth = personWidth;
this.personHeight = personHeight;
};
oImg.src = "images/person.png";
}
//运动方法
run(stepX, stepY) {
let canvasWidth = this.ctx.canvas.width;
let canvasHeight = this.ctx.canvas.height;
clearInterval(this.timeId);
this.timeId = setInterval(() => {
if (stepX !== 0) {
this.x += stepX;
}
if (stepY !== 0) {
this.y += stepY;
}
// 清除以前绘制的图片
this.ctx.clearRect(0, 0, canvasWidth, canvasHeight);
//重新绘制
this.ctx.drawImage(
this.oImg, // 需要绘制的图片
this.index * this.personWidth, //图片定位的位置
this.direction * this.personHeight, //图片定位的位置
this.personWidth, // 图片裁剪的大小
this.personHeight, // 图片裁剪的大小
this.x, // 图片绘制的位置
this.y, // 图片绘制的位置
this.personWidth, // 指定图片绘制大小
this.personHeight // 指定图片绘制大小
);
this.index++;
if (this.index > 3) {
this.index = 0;
}
}, 200);
}
moveDown() {
this.direction = 0;
this.run(0, 5);
}
moveUp() {
this.direction = 3;
this.run(0, -5);
}
moveLeft() {
this.direction = 1;
this.run(-5, 0);
}
moveRight() {
this.direction = 2;
this.run(5, 0);
}
//停止运动方法
stop() {
clearInterval(this.timeId);
}
}
let person = new Person(canvas, 100, 100);
person.render();
// person.moveDown();
// person.stop();
window.onkeydown = function (e) {
let key = e.key;
// console.log(key);
switch (key.toLowerCase()) {
case "w":
person.moveUp();
break;
case "s":
person.moveDown();
break;
case "a":
person.moveLeft();
break;
case "d":
person.moveRight();
break;
}
};
六、canvs形变
在canvas中所有的形变属性操作的都是坐标系, 而不是图形!!!
* {
margin: 0;
padding: 0;
}
canvas {
/* background: red; */
display: block;
margin: 0 auto;
margin: 50px auto;
}
<canvas width="500" height="400"></canvas>
// 1.拿到canvas
let canvas = document.querySelector("canvas");
// 2.从canvas中拿到绘图工具
let ctx = canvas.getContext("2d");
ctx.translate(100,0);//位移
ctx.scale(.8,.6);//缩放
ctx.rotate(Math.PI/6);//旋转
// 3.绘制一个矩形
ctx.strokeRect(100, 100, 200, 100);
七、事件绑定
因为整个canvas是一个标签, 所以只能通过监听鼠标在canvas上(事件只能绑定到canvas标签上)的位置来判断是否需要处理对应的图形,当然也可以通过第三方框架来解决交互问题,诸如: zrender.js / Knova.js /three.js / egret.js / pixi.js等等
isPointInPath方法如果开启了一个新的路径, 那么判断的就是该点是否在新的路径的图形中
示例:
*{
margin: 0;
padding: 0;
}
canvas{
display: block;
margin: 0 auto;
/* background: red; */
margin: 50px auto;
}
<canvas width="500" height="400">您的浏览器不支持canvas标签,请更换支持canvas标签的浏览器</canvas>
let oCanvas = document.querySelector("canvas");
// 2.从canvas中拿到绘图工具
let oCtx = oCanvas.getContext("2d");
// 3.绘制矩形
let rectX = 100;
let rectY = 100;
let rectWidth = 100;
let rectHeight = 100;
oCtx.rect(rectX, rectY, rectWidth, rectHeight);
oCtx.fill();
oCtx.beginPath();
oCtx.rect(200, 200, 100, 100);
oCtx.fill();
// 4.添加点击事件
oCanvas.onclick = function (event) {
let x = event.offsetX;
let y = event.offsetY;
if(x >= rectX && x <= rectX + rectWidth &&
y >= rectY && y <= rectY + rectHeight){
console.log("矩形被点击了");
}else{
console.log("矩形没有被点击");
}
console.log(oCtx.isPointInPath(x, y));
}