canvas图形

667 阅读4分钟

前言

课件地址

github.com/buglas/canv…

课堂目标

1.理解canvas 坐标系和栅格。

2.能够熟练绘制canvas 图形。

知识点

1.canvas 坐标系和栅格

2.canvas 的绘图形式 - 矩形

3.canvas 的绘图形式 - 路径

canvas 画布的坐标系和栅格

image-20220319174749902

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)

image-20220319175056716

代码实现:

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-绘制路径的步骤

  1. 开始建立路径:beginPath()

  2. 向路径集合中添加子路径:

    [

    moveTo(x,y); 形状; closePath() 可选,

    moveTo(x,y); 形状; closePath() 可选,

    moveTo(x,y); 形状; closePath() 可选,

    ]

  3. 显示路径:填充fill() ,描边stroke()

2-子路径的形状

  • 直线:lineTo(x,y); lineTo(x,y); lineTo(x,y)

image-20220319182040911

//画笔
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,半径,开始弧度,结束弧度,方向)

    image-20220319182138759

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,半径)

image-20220319182157424

ctx.beginPath();
ctx.moveTo(50,50);
ctx.arcTo(
    400,50,
    400,250,
    100
);
ctx.stroke();
  • 二次贝塞尔曲线:quadraticCurveTo(cpx1,cpy1,x,y)

image-20220319182226581

ctx.beginPath();
//起点
ctx.moveTo(50,50);
ctx.quadraticCurveTo(
        //控制点
    400,500,
    //结束点
    400,250,
)
ctx.stroke();
  • 三次贝塞尔曲线:bezierCurveTo(cpx1,cpy1,cpx2,cpy2,x,y)

image-20220319182446438

//起始点
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-总结

路径的绘图原理

image-20220319182649204

路径和子路径的概念

  • 路径:

    路径是子路径的集合。

    一个上下文对象同时只有一个路径,想要绘制新的路径,就要把当前路径置空。

    beginPath() 方法当前路径置空,也就是将路径恢复到默认状态,让之后绘制的路径不受以前路径的影响。

  • 子路径:

    子路径是一条只有一个起点的、连续不断开的线。

    moveTo(x,y) 是设置路径起点的方法,也是创建一条新的子路径的方法。

    路径里的第一条子路径可以无需设置起点,它的起点默认是子路径中的第一个点。

image-20220319182949971

案例1- 机器人

这样的机器人是我小时候喜欢画的,它就像摆积木一样,对基本图形进行拼凑,拼出我们想要的样子。

image-20220319184834082

<!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>

我们给机器人添加一个有趣的交互动画:默认机器人是忍者模式,当鼠标划上,将其调为愤怒模式。

1

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- 水滴

相较于上一个机器人的简单拼凑,这个水滴是对图形的入微级操作,因为它是用多个图形拼成了一个子路径。

image-20220319185617016

代码实现:

<!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>