写点什么

为了让师妹 20 分钟学会 canvas,我熬夜苦肝本文外加一个小项目【❤️建议收藏❤️】

作者:孤寒者
  • 2022 年 6 月 07 日
  • 本文字数:8809 字

    阅读完需:约 29 分钟

为了让师妹20分钟学会canvas,我熬夜苦肝本文外加一个小项目【❤️建议收藏❤️】

🎍0、前言

  1. 🎅🎅前几天小师妹突然火急火燎的的给我打电话说她在校招面试前端的时候,多次被提问到 canvas,但是她却不会,希望我能给她速成一下!可见 canvas 在前端的地已经越来越重要了!🎅🎅

  2. 🔆🔆所以我特地熬夜写了本文,就为了让大家能快速掌握 canvas(我的心里只有你们,没有小师妹)~🔆🔆

  3. 👙👙我会尽量把技术文写的通俗易懂/生动有趣,保证每一个想要学习知识 &&认认真真读完本文的读者们能够有所获,有所得。当然,如果你读完感觉本文写的还可以,真正学习到了东西,希望给我个「 赞 」 和 「 收藏 」,这个对我很重要,谢谢了!👙👙


👝1.初级阶段——师傅领进门~

🎣(1)canvas 是什么?

我们翻译一下这个单词,会发现它有「 画布 」的意思。画布画布不就是绘制图形的么?不过不同的是 canvas 元素是在网页上绘制图形!

  其实 canvas 元素就是使用 JavaScript 在网页上绘制图像。而绘制的画布区域是一个矩形区域,我们可以控制其中每一像素,以达到想画啥就画啥的效果。canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。


🍶(2)如何使用?

上面说 canvas 元素就是使用 JavaScript 在网页上绘制图像!所以关于 canvas 的使用也分为两步:

  第一步:在 HTML5 页面创建 canvas 元素;

  第二步:通过 JavaScript 来绘制。

1️⃣创建 Canvas 元素:

向 HTML5 页面添加 canvas 元素!规定此 canvas 元素的 id、宽度和高度(宽度和高度即指定画布的大小!):


<!--canvas默认大小:宽300px,高150px--><canvas id="myCanvas" width="200" height="100"></canvas>
复制代码

2️⃣通过 JavaScript 来绘制:

canvas 元素本身是没有绘图能力的。所有的绘制工作必须在 JavaScript 内完成:


<script type="text/javascript">  //第一步,匹配到canvas对象。  var c=document.getElementById("myCanvas");  //第二步,获取canvas的上下文环境   var cxt=c.getContext("2d");    //getContext("2d") 对象是内建的 HTML5 对象,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。    // 绘制一个红色的矩形:  cxt.fillStyle="red";  // fillRect方法是绘制矩形    参数:绘制矩形的左上角x坐标,y坐标,x方向长度,y方向长度  cxt.fillRect(0,0,150,75);  // 在画布上绘制 150x75 的矩形,从左上角开始 (0,0)。</script>
复制代码

3️⃣实现效果:

需要注意的是:canvas 的画布区域,左上角为坐标原点(0,0),分别向右为 x 轴,向下为 y 轴。


🚧2.中级阶段——绘制一些常见的基本图形~

  • 要知道,再复杂的图形也都是由各种基本图形组合而成。

  • 所以,先来看看如何绘制各种常见的基本图形。

  • 这样,进行一些需要使用 canvas 的项目设计时才会手到擒来!

🚓(1)绘制一条直线

  1. 指定起始(x,y)坐标;

  2. 指定粗细;

  3. 指定颜色。 

注意:通过 CSS 定位到 canvas 元素,并为其设置一个显眼的 border 样式,方便下面观察!

1️⃣上代码:

<!DOCTYPE html><html>  <head>    <meta charset="UTF-8">    <title></title>    <style type="text/css">      /*设置canvas画布样式——画布边框粗细1px;实线;红色*/      #myCanvas{        border: 1px solid red;      }    </style>  </head>  <body>    <canvas id="myCanvas" width="300" height="250"></canvas>  </body>  <script type="text/javascript">    var c=document.getElementById("myCanvas");    var ctx=c.getContext("2d");        // 1.首先,将画笔定位到起点坐标处;    ctx.moveTo(50,50);    // 2.然后,从当前位置连一条线到终点坐标处(注意:此时并没有真正的画线!)    ctx.lineTo(100,50);    ctx.strokeStyle = "blue";    // 线条的颜色    ctx.lineWidth = "5";       // 线条的粗细    // 3.最后,画线条,作用是描边————这句才是真正的画线!    ctx.stroke();              // 认真学习的同学可以将本行注释再看看效果哦!    </script></html>
复制代码

2️⃣实现效果:

🚑(2)绘制一条折线

  1. 指定折点处的形状;

  2. 指定线端点的形状。

1️⃣上代码:

<!DOCTYPE html><html>  <head>    <meta charset="UTF-8">    <title></title>    <style type="text/css">      #myCanvas{        border: 1px solid red;      }    </style>  </head>  <body>    <canvas id="myCanvas" width="300" height="250"></canvas>  </body>  <script type="text/javascript">    var c=document.getElementById("myCanvas");    var ctx=c.getContext("2d");        // 1.首先,将画笔定位到起点坐标处    ctx.moveTo(50,50);    // 2.然后,从当前位置连一条线到重点坐标处【因为现在画的是折线,所以拐两拐】(注意:并不是真画线!)    ctx.lineTo(100,50);    ctx.lineTo(60,80);        // 3.指定线段端点形状  ———— round:圆形;square:正方形;butt:默认    ctx.lineCap = "round";              // 4.指定线交点(折线点)的形状 ———— round:圆形;miter:默认;bevel:截取一部分;    ctx.lineJoin = "round";           ctx.strokeStyle = "blue";  // 线条的颜色    ctx.lineWidth = "5";     // 线条的粗细        // 5.最后,画线条,作用是描边————这句才是真正的画线!    ctx.stroke();            </script></html>
复制代码


认真学习的小伙伴们可以尝试将 lineCap 和 lineJoin 的值多次更改,看看各种值的真实实现效果哦!

2️⃣实现效果:

🚔(3)画矩形和圆

分别尝试画实心和空心!


同一张画图上绘制多个图形小知识点:

在同一个 canvas 上,即同一张画布上画图时,画笔的位置为画笔画完上一个图的结束点!

问题就是重新定位画笔过于麻烦!

解决方法——所以在一个图画完后使用.stroke()方法绘制出此图;并使用方法.beginPath()新开辟一次画图,重新定位画笔即可!

1️⃣上代码:

<!DOCTYPE html><html>  <head>    <meta charset="UTF-8">    <title>画方,画圆</title>        <style type="text/css">      #c1{        border:1px solid blue;                  }    </style>      </head>  <body>    <canvas id="c1" width="1300" height="700"></canvas>  </body>    <script type="text/javascript">    var c = document.getElementById("c1");    var ctx = c.getContext("2d");        //1.绘制空心矩形    ctx.strokeStyle = "blue";    ctx.lineWidth = 5;          //线条粗细    ctx.strokeRect(50,50,250,250);  //参数:左上角x,y坐标,x,y方向长度            //2.绘制实心矩形    ctx.fillStyle = "#DFFF4A";     // 指定填充颜色    ctx.fillRect(350,50,100,100);            //3.画一个既描边又填充的矩形    //第一种方法:先画一个空心矩形,然后在其内部画一个实心矩形    ctx.lineWidth = 20;    ctx.strokeRect(500,50,100,100);    ctx.fillRect(500,50,100,100);        //第二种方法:.rect()方法先画一个空心矩形,然后.fill()填充    ctx.rect(650,50,100,100);    ctx.stroke();    ctx.fill()            //4.绘制填充的圆    ctx.beginPath();    ctx.lineWidth = 3;    //参数:150,500是圆心,80是半径,0是弧的起始角度,  Math.PI/2是弧的结束角度, true是逆时针;false是顺时针。    ctx.arc(150,500,80,0,Math.PI*2,true);    ctx.stroke();    ctx.fill();      // 注意:使用的填充样式都是上面设置过的!            // 5.绘制空心的圆    ctx.beginPath();    ctx.lineWidth = 3;    ctx.arc(400,500,80,0,Math.PI*2,true);    ctx.stroke();
</script></html>
复制代码

2️⃣实现效果:

🚐(4)颜色渐变之线性渐变和发散渐变

渐变可以填充在矩形, 圆形, 线条, 文本等各种形状中,主要作用是:可以自己定义不同的颜色。

1️⃣上代码:

当我们使用渐变对象,必须使用两种或两种以上的停止颜色。addColorStop()方法指定颜色停止,参数使用坐标来描述,可以是 0 至 1。使用渐变,设置 fillStyle 或 strokeStyle 的值为渐变,然后绘制形状,如矩形,文本,或一条线。


<!DOCTYPE html><html>  <head>    <meta charset="UTF-8">    <title>线性渐变和发散渐变</title>    <style type="text/css">      #c1{        border: 3px solid red;      }    </style>  </head>  <body>    <canvas id="c1" width="1000" height="700"></canvas>  </body>    <script type="text/javascript">    //第一步,找到canvas对象。    var c = document.getElementById("c1");    //第二步,获取canvas的上下文环境    var ctx = c.getContext("2d");        //然后就可以通过ctx进行绘制了    ctx.beginPath();        // 线性渐变的实现    //第一种: 竖着渐变,从指定的左上角坐标到右上角坐标竖着范围内进行颜色渐变    var jianbian = ctx.createLinearGradient(100,100,300,100);  // 左上角x,y坐标,右上角x,y坐标    jianbian.addColorStop(0,"red");        jianbian.addColorStop(0.5,"blue");    jianbian.addColorStop(1,"yellow");    ctx.strokeStyle = jianbian;    ctx.lineWidth = 10;    ctx.moveTo(100,100);    ctx.lineTo(300,100);    ctx.stroke();        //第二种: 斜着渐变,从指定的左上角坐标到右下角坐标斜着范围内进行颜色渐变    ctx.beginPath();    var jianbian2 = ctx.createLinearGradient(400,300,600,500);    jianbian2.addColorStop(0,"cyan");    jianbian2.addColorStop(0.3,"green");    jianbian2.addColorStop(0.7,"purple");    jianbian2.addColorStop(1,"blue");    ctx.fillStyle = jianbian2;    ctx.moveTo(400,300);    ctx.lineTo(600,300);    ctx.lineTo(600,500);    ctx.lineTo(400,500);    ctx.closePath();        //使用.closePath()方法即可自动封闭图形,封闭终点坐标到起点坐标!    ctx.stroke();    ctx.fill();            // 发散渐变的实现    //指定两个圆所形成的圆环内发散向四周渐变    ctx.beginPath();    var jianbian3 = ctx.createRadialGradient(150,400,50,150,400,200);  // 参数:第一个圆的圆心+半径,第二个圆的圆心+半径    jianbian3.addColorStop(0,"red");    jianbian3.addColorStop(0.3,"blue");    jianbian3.addColorStop(0.7,"yellow");    jianbian3.addColorStop(1,"green");    ctx.fillStyle = jianbian3;    ctx.moveTo(10,300);    ctx.lineTo(310,300);    ctx.lineTo(310,500);    ctx.lineTo(10,500);    ctx.closePath();    ctx.stroke();    ctx.fill();      </script></html>
复制代码

2️⃣实现效果:

🚉拓展——几种复杂图形的绘制

  • 题目就是下面所列的五个,请小伙伴们发挥你们的小脑袋瓜,自己先动手绘制哦🙄!

  • 所用的操作在上面都已详细讲解,没有一道题超纲哦(实践是检验真理的唯一标准🤩)

  • 做完的小伙伴们可以看看下面我的代码和实现的效果,说不定你们做的比我的还要好😂!


  1. 使用红色填充的五角星;

  2. 使用渐变色填充的六边形;

  3. 机器人头部(使用线性渐变 &&发散渐变);

  4. 空心的五角星;

  5. 绘制一个四肢健全的小人。

1️⃣上代码:

<!DOCTYPE html><html>  <head>    <meta charset="UTF-8">    <title>五道题</title>    <style type="text/css">      #canvas_first{        border: 5px solid red;      }    </style>  </head>  <body>    <canvas id="canvas_first" width="1500" height="700"></canvas>  </body>    <script type="text/javascript">    // 想要在canvas上画东西,第一步要先找到canvas控件    var c = document.getElementById("canvas_first");    //获取绘制的上下文环境,简单理解为画笔(实际上不是画笔)    var ctx = c.getContext("2d");  //此对象是内建的 HTML5 对象,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。        // 1.五角星    ctx.moveTo(100,0);      // 将画笔移动到指定的位置    ctx.fillStyle = "red";        // 封闭图形填充的颜色   配合.fill()使用    ctx.lineTo(159,181);      //从当前位置连一条线到指定位置(并不是真画线)    ctx.lineTo(5,69);    ctx.lineTo(195,69);    ctx.lineTo(41,181);    ctx.closePath();    ctx.strokeStyle = "red";  // 线条的颜色    ctx.stroke();        //画线条,作用是描边。    ctx.fill();            //填充封闭区域,使用指定的填充样式来填充              // 2.六边形    ctx.beginPath();    var jianbian2 = ctx.createLinearGradient(250,20,450,220);   // 线性渐变————左上角到右下角渐变    jianbian2.addColorStop(0,"cyan");    jianbian2.addColorStop(0.3,"green");    jianbian2.addColorStop(0.7,"purple");    jianbian2.addColorStop(1,"blue");    ctx.fillStyle = jianbian2;    ctx.moveTo(300, 20);    ctx.lineTo(400,20);    ctx.lineTo(450, 120);    ctx.lineTo(400,220);    ctx.lineTo(300,220);    ctx.lineTo(250,120);    ctx.closePath();    ctx.strokeStyle = "black";    ctx.lineWidth = "3";    // 设置线条的粗细    ctx.stroke();    ctx.fill();            // 3.机器人头    ctx.beginPath();      // 开启新的路径    // 头轮廓    ctx.strokeRect(500,20,250,250);    //左眼    var jianbian3 = ctx.createRadialGradient(560,80,5,560,80,35);    // 发散渐变——————指定两组圆心+半径:环内渐变    jianbian3.addColorStop(0,"red");    jianbian3.addColorStop(0.3,"blue");    jianbian3.addColorStop(0.7,"yellow");    jianbian3.addColorStop(1,"green");    ctx.fillStyle = jianbian3;    ctx.arc(560,80,35,0,Math.PI*2,true);    // 150,500是圆心,   80是半径,  0是弧的起始角度,  Math.PI/2是弧的结束角度, true是逆时针;false是顺时针.    //右眼    ctx.strokeRect(653,46,70,70);    //鼻子    ctx.moveTo(620,145);    ctx.lineTo(640,160);    ctx.lineTo(602,160);    ctx.closePath();      //从当前位置连接一条线,到起始位置,形成一个封闭路径    ctx.lineJoin = "round";   //线交点图形      ctx.stroke();    ctx.fill();    //嘴巴    ctx.beginPath();    var jianbian = ctx.createLinearGradient(568,220,683,220);    // 线性渐变——————如果是一条直线,直接起点到终点渐变即可(注意:)    jianbian.addColorStop(0,"red");    jianbian.addColorStop(0.5,"blue");    jianbian.addColorStop(1,"yellow");    ctx.strokeStyle = jianbian;    ctx.lineWidth = 20;    ctx.moveTo(568,220);    ctx.lineTo(683,220);    ctx.lineCap = "round";    //线端点的显示效果    ctx.stroke();
// 4.空心五角形 ctx.beginPath(); ctx.lineWidth = 3; ctx.strokeStyle = "black"; ctx.moveTo(750,300); ctx.lineTo(790,380); ctx.lineTo(870,380); ctx.lineTo(800,430); ctx.lineTo(840,520); ctx.lineTo(750,460); ctx.lineTo(670,520); ctx.lineTo(700,430); ctx.lineTo(630,380); ctx.lineTo(710,380) ctx.closePath(); ctx.stroke() // 5.一个四肢健全的小人 ctx.beginPath(); // 脑袋 ctx.arc(1150,150,80,0,Math.PI*2,true); // 身体 ctx.strokeRect(1070,230,160,250); // 身体的纽扣 ctx.strokeRect(1143,290,10,20); ctx.strokeRect(1143,350,10,20); ctx.strokeRect(1143,410,10,20); // 腿 ctx.strokeRect(1165,480,45,120); ctx.strokeRect(1090,480,45,120); // 脚 ctx.strokeRect(1158,600,60,25); ctx.strokeRect(1082,600,60,25); // 右胳膊+手 ctx.strokeRect(1230,290,110,35); ctx.strokeRect(1310,325,30,78); // 左胳膊+手 ctx.strokeRect(960,290,110,35); ctx.strokeRect(960,325,30,78); ctx.stroke(); // 小脸 ctx.beginPath(); // 左眼 ctx.arc(1115,125,20,0,Math.PI,true); // 右眼 ctx.moveTo(1200,125); ctx.arc(1180,125,20,0,Math.PI,true); // 嘴巴 ctx.moveTo(1180,170); ctx.arc(1150,170,30,0,Math.PI,false); ctx.stroke();
</script></html>
复制代码

2️⃣实现效果:

👝3.高级阶段——一个小项目助你玩透 canvas~

🏆(1)实现橡皮擦

1️⃣难点:如何清除 canvas 画布上指定区域:

使用 clearRect() 方法清空指定矩形区域。

JavaScript 语法:  context.clearRect(x,y,width,height)


⚠️难点解决——代码实现:

<!DOCTYPE html><html>  <head>    <meta charset="UTF-8">    <title></title>    <style type="text/css">      #c{        border: 1px solid red;      }    </style>  </head>  <body>    <canvas id="c" width="1000" height="700"></canvas>  </body>  </body>    <script type="text/javascript">    var c = document.getElementById("c");    var ctx = c.getContext("2d");        // 填充整个canvas为lightgray颜色    ctx.fillStyle = "lightgray";    ctx.fillRect(0,0,1000,700);        // 清除指定区域中的内容    ctx.clearRect(100,100,100,100)  </script></html>
复制代码

⚠️难点解决——实现效果:

2️⃣项目——代码实现:

实现效果——实现橡皮擦,即鼠标点下去移动所经过位置擦除,鼠标松开不清除。


  • 本项目实现时需要注意的是:

  • 在监听 canvas 的 onmousemove 事件(鼠标移动事件)时:

  •   c.onmousemove = function(e){ e.clientX; e.pageX; c.offsetLeft; }

  • 关于获取到的关于鼠标移动信息的几个量 e.clientX; e.pageX;和 c.offsetLeft;都不能直接作为判断鼠标点击处区域的清除:

  • 第一个原因是:上述几个量各自代表的含义如下:(它们的含义决定它们不是鼠标相对于 canvas 的真实坐标!)


  1. e.clientX 是鼠标距离网页窗口左边缘的距离 所以不能作为定位鼠标位置的量,因为窗口可以上下左右滑动,画布位置相对于窗口会变;

  2. e.pageX 是鼠标距离网页内容左边缘的距离;

  3. c.offsetLeft 是 canvas 相对于浏览器左边的距离


  • 所以我们通过组合使用 e.pageX 和 c.offsetLeft 即可实现鼠标真实坐标的定位。在此要理解一个点是:canvas 画布的坐标轴并不是直接与网页的坐标轴重合!所以才会有 e.pageX(以网页的坐标轴为参考系)和 c.offsetLeft(以 canvas 画布的坐标系为参考系)两种量。而我们只需要使用 e.pageX-c.offsetLeft(理解为将 canvas 画布的坐标轴向左上网页的坐标轴平移直至重合)即可定位到鼠标的真实坐标。


  • 第二个原因是:context.clearRect(x,y,w,h);方法清除的是指定坐标(x,y),宽 w,高 h 的矩形区域。

  • 注意:这个宽是指以(x,y)坐标向右 x 轴的长度;这个高是指以(x,y)坐标向下 y 轴的长度。所以我们定位到的鼠标坐标应该在 x 轴和 y 轴同时向左上 canvas 画布的原点处移动 w/2 和 h/2 个像素长。即 e.pageX-c.offsetLeft - w/2,这就是清除区域的 x 坐标的推理过程。y 坐标类似,小伙伴们自己多加思考哦!  


<!DOCTYPE html><html>  <head>    <meta charset="UTF-8">    <title></title>    <style type="text/css">      #c{        border: 1px solid red;      }    </style>  </head>  <body>    <canvas id="c" width="1000" height="700"></canvas>  </body>    <script type="text/javascript">    var c = document.getElementById("c");    var ctx = c.getContext("2d");        // 填充整个canvas为lightgray颜色    ctx.fillStyle = "lightgray";    ctx.fillRect(0,0,1000,700);        // 清除指定区域中的内容    //ctx.clearRect(100,100,100,100)
// 监听canvas的onmousedown事件(鼠标点击事件) c.onmousedown = function(ev){ c.onmousemove = function(e){ // .onmousemove是鼠标移动事件 console.log(e); // 控制台打印e会发现它是鼠标移动所有信息的一个量 // e.clientX是鼠标距离网页窗口左边缘的距离 所以不能作为定位鼠标位置的量,因为窗口可以上下左右滑动,画布位置相对于窗口会变! // e.clientY是鼠标距离网页窗口上边缘的距离 // e.pageX是鼠标距离网页内容左边缘的距离 // e.pageY是鼠标距离网页内容上边缘的距离 // c.offsetLeft是canvas相对于浏览器左边的距离 // c.offsetTop是canvas相对于浏览器上方的距离 var w = 20; // 清除区域的宽度 var h = 20; // 清除区域的高度 var x = e.pageX-c.offsetLeft - w/2; // 清除区域的x位置 var y = e.pageY-c.offsetTop - h/2; // 清除区域的y位置 ctx.clearRect(x,y,w,h); } } c.onmouseup = function(ev){ // .onmouseup是鼠标松开事件 // 取消onmousemove事件 c.onmousemove = null; } </script> </html>
复制代码

3️⃣项目——实现效果:

  • 自己跟着步骤做就可以看到效果啦~

🔆4.In The End——心灵鸡汤送上~


从现在做起,坚持下去,一天进步一小点,不久的将来,你会感谢曾经努力的你!


本博主会持续更新爬虫基础分栏及爬虫实战分栏,认真仔细看完本文的小伙伴们,可以点赞收藏并评论出你们的读后感。并可关注本博主,在今后的日子里阅读更多爬虫文!


如有错误或者言语不恰当的地方可在评论区指出,谢谢!如转载此文请联系我征得本人同意,并标注出处及本博主名,谢谢 !

发布于: 2022 年 06 月 07 日阅读数: 43
用户头像

孤寒者

关注

同名微信公众号【孤寒者】 2021.11.09 加入

HDZ核心组成员 华为云享专家、Python全栈领域博主 CSDN原力计划作者、CSDN全栈领域优质创作者 专注分享Python领域原创系列文章,如Python爬虫、Django、tornado、flask等。

评论

发布
暂无评论
为了让师妹20分钟学会canvas,我熬夜苦肝本文外加一个小项目【❤️建议收藏❤️】_前端_孤寒者_InfoQ写作社区