前言
课件地址
课堂目标
1.理解canvas 坐标系和栅格。
2.能够熟练绘制canvas 图形。
知识点
1.canvas 坐标系和栅格
2.canvas 的绘图形式 - 矩形
3.canvas 的绘图形式 - 路径
canvas 画布的坐标系和栅格
canvas 的坐标系是二维直角坐标系,由x轴和y轴组成。
canvas 坐标系中横向的轴为x轴,越往右越大;竖向的轴为y 轴,越往下越大。
canvas 坐标系是以像素的宽高为基底的。
栅格就是上图的4 个格子,每一个格子就是一个像素,像素具有rgba 数据。
canvas 画布的像素的数量等于画布的宽度乘以高度。
矩形
这里说的矩形是canvas 绘图形式中的矩形,它有三种类型:
- 填充矩形方法:fillRect(x,y,w,h)
- 描边矩形方法:strokeRect(x,y,w,h)
- 清理矩形方法:clearRect(x,y,w,h)
代码实现:
const canvas=document.getElementById('canvas');
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
//画笔
const ctx=canvas.getContext('2d');
/*填充矩形方法:fillRect(x,y,w,h)*/
ctx.fillRect(
100,50,
400,200
)
/*描边矩形方法:strokeRect(x,y,w,h)*/
ctx.strokeStyle='red';
ctx.lineWidth=10;
ctx.strokeRect(
100,50,
400,200
)
/*清理矩形方法:clearRect(x,y,w,h)*/
ctx.clearRect(
100,50,
400,200
)
路径
1-绘制路径的步骤
-
开始建立路径:beginPath()
-
向路径集合中添加子路径:
[
moveTo(x,y); 形状; closePath() 可选,
moveTo(x,y); 形状; closePath() 可选,
moveTo(x,y); 形状; closePath() 可选,
]
-
显示路径:填充fill() ,描边stroke()
2-子路径的形状
- 直线:lineTo(x,y); lineTo(x,y); lineTo(x,y)
//画笔
const ctx=canvas.getContext('2d');
//线宽
ctx.lineWidth=10;
//直线
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(400,50);
ctx.lineTo(400,250);
ctx.closePath();
ctx.stroke();
ctx.fill();
-
圆弧:arc(x,y,半径,开始弧度,结束弧度,方向)
ctx.beginPath();
ctx.arc(
300,300,
200,
0,Math.PI*3/2,
);
ctx.moveTo(300+200,500);
ctx.arc(
300,500,
200,
0,Math.PI*3/2,
);
ctx.stroke();
- 切线圆弧:arcTo(x1,y1,x2,y2,半径)
ctx.beginPath();
ctx.moveTo(50,50);
ctx.arcTo(
400,50,
400,250,
100
);
ctx.stroke();
- 二次贝塞尔曲线:quadraticCurveTo(cpx1,cpy1,x,y)
ctx.beginPath();
//起点
ctx.moveTo(50,50);
ctx.quadraticCurveTo(
//控制点
400,500,
//结束点
400,250,
)
ctx.stroke();
- 三次贝塞尔曲线:bezierCurveTo(cpx1,cpy1,cpx2,cpy2,x,y)
//起始点
ctx.moveTo(50,50);
ctx.bezierCurveTo(
//控制点1
400,50,
//控制点2
400,250,
//结束点
600,250
)
ctx.stroke();
- 矩形:rect(x,y,w,h)
ctx.beginPath();
ctx.rect(
100,50,
400,200
)
ctx.rect(
100,350,
400,200
)
ctx.stroke();
rect(x,y,w,h) 绘制路径时,会内置moveTo() 功能。
3-总结
路径的绘图原理
路径和子路径的概念
-
路径:
路径是子路径的集合。
一个上下文对象同时只有一个路径,想要绘制新的路径,就要把当前路径置空。
beginPath() 方法当前路径置空,也就是将路径恢复到默认状态,让之后绘制的路径不受以前路径的影响。
-
子路径:
子路径是一条只有一个起点的、连续不断开的线。
moveTo(x,y) 是设置路径起点的方法,也是创建一条新的子路径的方法。
路径里的第一条子路径可以无需设置起点,它的起点默认是子路径中的第一个点。
案例1- 机器人
这样的机器人是我小时候喜欢画的,它就像摆积木一样,对基本图形进行拼凑,拼出我们想要的样子。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>机器人</title>
<style>
body{margin: 0;overflow: hidden;}
#canvas{background: antiquewhite;}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
const canvas=document.getElementById('canvas');
//canvas充满窗口
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
//画笔
const ctx=canvas.getContext('2d');
ctx.translate(100,100);
// ctx.scale(-1,1)
ctx.rotate(Math.PI/24)
//颜色
//填充色
ctx.fillStyle='red';
//描边宽度
ctx.lineWidth=40;
/*
面部
填充方法:fillRect(x,y,w,h)
*/
ctx.fillRect(
50,250,
400,200
);
/*
面部轮廓
描边方法:strokeRect(x,y,w,h)
*/
ctx.strokeRect(
50,250,
400,200
);
/*
眼罩
清理矩形方法:clearRect(x,y,w,h)
*/
ctx.clearRect(
50,300,
400,60
)
/*
嘴巴
直线:lineTo(x,y)
*/
ctx.beginPath();
ctx.moveTo(150,400);
ctx.lineTo(350,400);
ctx.stroke();
/*
眼睛
arc(x,y,半径,开始弧度,结束弧度,方向)
*/
ctx.beginPath();
ctx.arc(
150,330,
20,
0,Math.PI*2
);
ctx.moveTo(350-20,340);
ctx.arc(
350,340,
20,
Math.PI,0
);
ctx.fill();
/*
天线
bezierCurverTo(cpx1,cpy1,cpx2,cpy2,x,y)
*/
ctx.beginPath();
ctx.moveTo(50,50);
ctx.bezierCurveTo(
150,50,
150,250,
250,250
);
ctx.moveTo(450,50);
ctx.bezierCurveTo(
350,50,
350,250,
250,250
);
ctx.stroke();
</script>
</body>
</html>
我们给机器人添加一个有趣的交互动画:默认机器人是忍者模式,当鼠标划上,将其调为愤怒模式。
const canvas=document.getElementById('canvas');
//canvas充满窗口
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
//画笔
const ctx=canvas.getContext('2d');
//绘图
function render(){
//描边宽度
ctx.lineWidth=40;
//矩形
//面部矩形
ctx.fillRect(50,250,400,200);
//面部轮廓
ctx.strokeRect(50,250,400,200);
//面部擦除
ctx.clearRect(50,300,400,60);
//路径
//直线
ctx.beginPath();
ctx.moveTo(150,400);
ctx.lineTo(350,400);
ctx.stroke();
//圆形
ctx.beginPath();
ctx.arc(150,330,20,0,Math.PI*2);
ctx.fill();
//半圆
ctx.beginPath();
ctx.arc(350,340,20,-Math.PI,0);
ctx.fill();
//三次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(50,50);
ctx.bezierCurveTo(150,50,150,250,250,250);
ctx.moveTo(450,50);
ctx.bezierCurveTo(350,50,350,250,250,250);
ctx.stroke();
}
render();
canvas.addEventListener('mousemove',function({clientX,clientY}){
if(clientX>30&&clientX<470&&clientY>230&&clientY<470){
ctx.fillStyle='red';
}else{
ctx.fillStyle='black';
}
ctx.clearRect(0,0,canvas.width,canvas.height);
render();
})
案例2- 水滴
相较于上一个机器人的简单拼凑,这个水滴是对图形的入微级操作,因为它是用多个图形拼成了一个子路径。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>水滴</title>
<style>
html{height: 100%;overflow: hidden}
body{height: 100%;margin: 0;}
#canvas{
background: antiquewhite;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
const canvas=document.getElementById('canvas');
//canvas充满窗口
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
//画笔
const ctx=canvas.getContext('2d');
ctx.fillStyle='#00acec';
/*
三次贝塞尔曲线:bezierCurverTo(cpx1,cpy1,cpx2,cpy2,x,y)
圆弧:arc(x,y,半径,开始弧度,结束弧度,方向)
*/
//变换-位移
// ctx.rotate(Math.PI/12);
ctx.translate(300,300);
//开始新路径
ctx.beginPath();
//指定起点,建立子路径
ctx.moveTo(0,0);
//绘制二次贝塞尔曲线
ctx.quadraticCurveTo(50,-50,50,-100);
//绘制圆弧
ctx.arc(0,-100,50,0,Math.PI,true);
//绘制二次贝塞尔曲线
ctx.quadraticCurveTo(-50,-50,0,0);
//显示路径
// ctx.stroke();
ctx.fill();
</script>
</body>
</html>