想象一下,用几行代码就能创造出如此逼真的图像和动画,仿佛将艺术与科技完美融合,前端开发的Canvas技术正是这个数字化时代中最具魔力的一环,它不仅仅是网页的一部分,更是一个无限创意的画布,一个让你的想象力自由驰骋的平台。
目录
初识canvas
路径绘制
贝塞尔曲线
封装路径(Path2D)
初识canvas
身为一个WEB开发人员,肯定都是想着能够开发出酷炫和激动人心的应用程序来,想开发出很多动画特效,例如黑客帝国的数字,彩色炫酷的例子动效。也可以实现各种图画面板,如实现类似于photoshop的web在线图像编辑。各种酷炫的表单等等,接下来我们通过一段简单的代码来了解如何实现一个画布:
通过canvas标签创建一个画布,通过getContext函数设置2d画笔,然后通过fillRect函数创建一个画布的位置与大小,代码如下:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");// 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 绘制图形,矩形 fillRect(x, y, width, height) 参数位置x,位置y,宽度,高度ctx.fillRect(100, 200, 300, 300);</script>
</body>
执行代码在浏览器打开的界面得到的结果如下所示,可以看到我们在浏览器得到一个黑色的区域,在这我们可以看到,我们在canvas设置的宽高是画布的初始宽高,这里可以传递实际宽高,相当于一张图片有一个初始宽高,可以对这张图片进行放大缩小操作:
当然我们也可以对 fillRect 函数继续拆开来书写,代码如下达到的效果是一样的:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");// 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 绘制图形ctx.rect(100, 200, 300, 300); // 绘制矩形ctx.fillStyle = "red"; // 填充颜色ctx.fill(); // 填充</script>
</body>
如果想要清除画布中的样式的话,可以采用clearRect函数的方法进行,这里做一个简单的代码示例:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");// 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 绘制图形ctx.fillRect(100, 100, 200, 200);let height = 0let t1 = setInterval(() => {height++ctx.clearRect(0, 0, canvas.clientWidth, height);if (height > canvas.clientHeight) {clearInterval(t1)}}, 10)</script>
</body>
得到的效果如下所示:
浏览器兼容性:至今已经2024年了,我相信大家的电脑浏览器已经普遍是最新版本的浏览器,除非长期使用比较古老的浏览器或者说很久都没有更新过浏览器的人来说,可能存在不兼容canvas的情况。因为只有canvas才有getContext函数,所以这里我们也可以对canvas做一个判断:
let canvas = document.getElementById("canvas");
// 判断是否有getContext
if (canvas.getContext) {console.log("当前浏览器不支持canvas,请下载最新浏览器");
}
当然这里我们也可以直接写在canvas标签中,因为如果canvas不能被识别的情况下会将canvas作为一个普通的标签进行执行,所以会直接呈现canvas标签内部的内容:
<canvas id="canvas" width="600" height="400">当前浏览器不支持canvas,请下载最新浏览器<a href="www.baidu.com">立即下载</a>
</canvas>
当然canvas还有许多API函数可以使用,如果忘记的话,可以查阅 MDN ,进行查看:
路径绘制
在我们设置画布的时候,画布内部是有规定的坐标的,我们可以通过拿到特定的坐标可以绘制一个特定的曲线,所以我们需要告诉canvas的画笔坐标的落点是在哪里?我们要在什么样的位置画什么样的图案。
在我们开始画图之前,我们需要了解一下画布栅格(canvasgrid)以及坐标空间,如下图所示,canvas元素默认被网格所覆盖。通常来说网格中的一个单元相当于canvas元素中的一像素。
栅格的起点为左上角(坐标为(0,0)),所有元素的位置都相对于原点定位,所以图中蓝色方形左上角的坐标为距离左边(X轴)×像素,距离上边(Y轴)y像素(坐标为(x,y)):
绘制矩形:可以通过strokeRect函数进行,这里通过如下代码示例进行演示:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");// 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 绘制图形,路径绘制矩形 strokeRect(x1, y1, 矩形宽度, 矩形高度)ctx.strokeRect(100, 100, 200, 100);</script>
</body>
在页面得到的结果如下所示:
绘制圆形:当然如果想绘制圆形的话可以采用如下的代码方式,使用arc函数绘制圆弧传递6个参数即可:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");// 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 绘制圆弧 圆形x坐标 y坐标 半径 起始角度 结束角度 顺时针/逆时针(默认顺时针)console.log(Math.PI / 2)ctx.arc(300, 200, 50, 0, Math.PI / 2, true) ctx.stroke()</script>
</body>
得到的画面如下所示:
如果想绘制一个简单的笑脸的话,可以采用如下的方式进行,因为canvas是连续的,也就是画笔是不会自动抬起而是一直画下去,这里需要我们手动的指定画笔的抬起进行书写样式,具体代码如下所示:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");// 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 绘制圆脸ctx.beginPath(); // 开始绘制ctx.arc(75, 75, 50, 0, Math.PI * 2);ctx.stroke()ctx.closePath(); // 闭合路径ctx.beginPath();// 绘制嘴巴ctx.arc(75, 75, 35, 0, Math.PI);ctx.stroke()ctx.closePath(); ctx.beginPath();// 绘制左眼ctx.arc(60, 65, 5, 0, Math.PI * 2);ctx.stroke()ctx.closePath(); ctx.beginPath();// 绘制右眼ctx.arc(90, 65, 5, 0, Math.PI * 2);ctx.closePath(); ctx.stroke()</script>
</body>
绘制线段:如果想绘制一条从当前位置到指定x以及y位置的直线,可以采用的方式是lineTo方法:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");// 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 绘制线段ctx.moveTo(300, 200); // 起点ctx.lineTo(350, 250); // 终点 起点ctx.lineTo(350, 200); // 终点 起点ctx.lineTo(300, 200); // 终点ctx.stroke();</script>
</body>
最终呈现的效果如下所示:
绘制圆弧:当然这里我们也可以通过arcTo方法进行绘制圆弧,给出如下代码示例:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas");// 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 绘制线段ctx.moveTo(300, 200); // 第一个点ctx.arcTo(300, 250, 250, 250, 25); // 第二个和第三个点以及圆弧半径 ctx.stroke();</script>
</body>
其大致的意思如下,三个点组成的线段相切得到的圆弧片段就是我们想要的:
贝塞尔曲线
贝塞尔曲线是一种数学曲线,通常用于在计算机图形学和计算机辅助设计(CAD)中绘制平滑曲线,它由法国工程师皮埃尔·贝塞尔(Pierre Bézier)在20世纪60年代提出并广泛应用于汽车工业的设计中,贝塞尔曲线通过控制点(称为贝塞尔控制点)来定义曲线的形状,其形式简单且易于计算,因此在计算机图形学中得到了广泛应用,如绘制曲线、字体设计、动画制作等领域,在canvas中贝塞尔曲线也是被广泛应用,本文就其做一个简单的概述供大家学习了解:
二次贝塞尔曲线:由三个控制点定义,形状相对简单。
1)由三个控制点定义:起始点P0,控制点P1,终点P2。
2)曲线不经过P1点,但P1点影响曲线的形状。
3)用途:二次贝塞尔曲线通常用于较简单的曲线绘制和动画路径。
这边我们可以做一个气泡聊天框进行一个简单的练习,详细的代码如下所示:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas"); // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 初始起点ctx.moveTo(200, 300)// 绘制二次贝塞尔曲线/* 二次贝塞尔曲线 ctx.quadraticCurveTo(cx, cy, x, y) 中,四个参数的含义如下:cx: 控制点的 x 坐标。这个点影响曲线的弯曲方向和弧度。cy: 控制点的 y 坐标。与 cx 一起决定了曲线的形状。x: 曲线的终点 x 坐标。这是曲线将要到达的目标点。y: 曲线的终点 y 坐标。与 x 一起决定了曲线的最终位置。*/ctx.quadraticCurveTo(150, 300, 150, 200); ctx.quadraticCurveTo(150, 100, 300, 100); ctx.quadraticCurveTo(450, 100, 450, 200); ctx.quadraticCurveTo(450, 300, 250, 300); ctx.quadraticCurveTo(250, 350, 150, 350); ctx.quadraticCurveTo(200, 350, 200, 300); ctx.stroke()</script>
</body>
程序的效果如下所示,效果还不错:
三次贝塞尔曲线:由四个控制点定义,更能描述复杂的曲线。
1)由四个控制点定义:起始点P0,控制点P1,控制点P2,终点P3。
2)曲线经过起始点P0和终点P3,但不一定经过控制点P1和P2。
3)用途:三次贝塞尔曲线比二次更灵活,适用于更复杂的曲线绘制,如字体设计、CAD绘图、动画路径等。
这里我们可以借助三次贝塞尔曲线做一个简单的爱心:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas"); // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");// 初始起点ctx.moveTo(300, 200)/* 第一个控制点和第二个控制点是曲线弯曲方向的关键点第三个控制点 终点是曲线最终到达的目标点*/ctx.bezierCurveTo(350, 150, 400, 200, 300, 300); ctx.bezierCurveTo(200, 200, 250, 150, 300, 200); ctx.stroke()</script>
</body>
程序的效果如下所示,效果还不错:
二次和三次贝塞尔曲线在计算机图形学中广泛用于平滑曲线的生成和路径控制,它们被用来设计字体、绘制图形、创建动画路径、制作CAD图形等,其简单的数学形式和良好的平滑性使它们成为计算机图形学中不可或缺的工具之一。
封装路径(Path2D)
在canvas中,Path2D是一个对象用来存储和重用路径描述信息,从而方便地绘制相同或类似的路径多次,尤其适合于需要频繁使用复杂路径的场景,这里我们做一个简单的概述:
通过如下代码把路径存在2d对象中,下次想要操作这个路径,就直接把2d对象传给fill或者描边方法就行了,如下给出代码示例:
<body><canvas id="canvas" width="600" height="400"></canvas><script>let canvas = document.getElementById("canvas"); // 获取2维画笔,上下文对象let ctx = canvas.getContext("2d");let heartPath = new Path2D();// 初始起点heartPath.moveTo(300, 200)heartPath.bezierCurveTo(350, 150, 400, 200, 300, 300); heartPath.bezierCurveTo(200, 200, 250, 150, 300, 200); ctx.stroke(heartPath)let chatPath = new Path2D();chatPath.moveTo(200, 300)chatPath.quadraticCurveTo(150, 300, 150, 200); chatPath.quadraticCurveTo(150, 100, 300, 100); chatPath.quadraticCurveTo(450, 100, 450, 200); chatPath.quadraticCurveTo(450, 300, 250, 300); chatPath.quadraticCurveTo(250, 350, 150, 350); chatPath.quadraticCurveTo(200, 350, 200, 300); ctx.stroke(chatPath)ctx.fill(heartPath)</script>
</body>
页面呈现的效果如下所示: