Canvas简单入门
source link: https://www.clzczh.top/2022/05/29/Canvas/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Canvas简单入门
创建canvas
至少需要提供width
和height
属性,才能通知浏览器需要多大位置画图。标签的内容是后备数据,在浏览器不支持canvas
元素时显示。
<canvas id="mycanvas" width="200" height="200">haha</canvas>
可以通过if(canvas.getContext)
来判断浏览器是否支持canvas
。
通过canvas.getContext('2d')
可以获取 2D 绘图上下文。2D 绘图上下文提供了绘制 2D 图形的方法。左边原点(0, 0)在 canvas
元素的左上角,x 坐标向右增长,y 坐标向下增长。
从画布上导出一张 PNG 格式的图片
<body>
<canvas id="mycanvas" width="200" height="200">haha</canvas>
<script>
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
// 获得图像的数据URI
const imgURI = mycanvas.toDataURL("image/png");
console.log(imgURI);
}
</script>
</body>
我们查看控制台可以发现,输出了一串base64
编码,也就是说,canvas.toDataURL
就是将画布 canvas
转换成base64
编码。
填充与描边
填充就是以特定的样式填充形状,包括颜色、渐变、图像
描边就是只给形状边界着色。
显示效果取决于两个属性:fillStyle
和strokeStyle
。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
context.fillStyle = "#000";
context.strokeStyle = "red";
}
没有效果?
别急,这是因为我们只是设置了填充和描边而已,想要它生效,还需要绘制出来才能有效果。
与绘制矩形相关的方法有三个。它们都接收 4 个参数:矩形 x 坐标、矩形 y 坐标、矩形宽度和矩形高度。(单位是像素,但是传参时不需要传单位)
fillRect
strokeRect
clearRect
fillRect
:绘制并填充矩形
fillRect
:以指定颜色在画布上绘制并填充矩形,填充色使用fillStyle
来设置。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
context.fillStyle = "pink";
context.fillRect(10, 10, 50, 50);
context.fillStyle = "rgba(0, 0, 0, .1)";
context.fillRect(30, 30, 50, 50);
}
stokeRect
:绘制矩形轮廓
stokeRect
:绘制矩形轮廓,颜色由strokeStyle
来指定。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
context.strokeStyle = "red";
// 设置描边宽度
context.lineWidth = 5;
context.strokeRect(10, 10, 50, 50);
context.strokeStyle = "blue";
context.fillStyle = "rgba(0, 0, 0, .1)";
context.strokeRect(30, 30, 50, 50);
}
clearRect
:擦除画布中某个区域
clearRect
:擦除画布中某个区域,把擦除的区域变透明。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
context.fillStyle = "red";
context.fillRect(0, 0, 200, 200);
context.clearRect(50, 50, 100, 100);
}
绘制路径需要先调用beginPath
,表示要开始绘制路径,再调用以下方法来绘制路径。
lineTo(x, y)
:绘制一条从上一个点到(x, y)的直线moveTo(x, y)
:不绘制线条,只是把画笔移动到(x, y)- 更多
绘制完路径后,可以指定fillStyle
属性并调用fill
方法来填充路径,也可以指定strokeStyle
属性并调用stoke
方法来描画路径。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
// 创建路径
context.beginPath();
// 绘制圆弧,参数分别是圆心x坐标、圆形y坐标、圆弧半径、圆弧起始点(单位:弧度)、圆弧终点(单位:弧度)、绘制方向(false为顺时针绘制,true为逆时针绘制)
context.arc(100, 100, 99, 0, 2 * Math.PI, true);
// context.fillStyle = 'pink'
// context.fill()
context.strokeStyle = "pink";
context.stroke();
}
还可以调用clip
方法创建一个新的剪切区域。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
// 创建路径
context.beginPath();
// 绘制圆弧,参数分别是圆心x坐标、圆形y坐标、圆弧半径、圆弧起始点(单位:弧度)、圆弧终点(单位:弧度)、绘制方向(false为顺时针绘制,true为逆时针绘制)
context.arc(100, 100, 50, 0, 2 * Math.PI, true);
context.fillStyle = "pink";
context.clip();
context.fillRect(0, 0, 100, 100);
}
上面的扇形怎么出来的呢?
我们可以把clip
变成fill
,看下没有被剪切的话,是什么样子。
也就是说,实际上剪切就是两个图形相交部分。
如果使用lineTo
需要注意:没有设置moveTo
时,这个位置并不是(0, 0),而是空,所以第一次的lineTo
没法画出结果。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
// 创建路径
context.beginPath();
// context.moveTo(0, 0);
context.lineTo(100, 50);
context.lineTo(200, 0);
context.lineWidth = 8;
context.strokeStyle = "pink";
// 描画路径
context.stroke();
}
没有moveTo
:
有moveTo
:
beginPath 的作用
上面的例子中,beginPath
并没有作用,也就是说上面的例子中,其实有没有beginPath
都一样。那么beginPath
有什么作用呢?
beginPath
表示下面绘制的图形是一个新的路径。具体看下实例。
const context = mycanvas.getContext("2d");
// 创建路径
context.beginPath();
context.moveTo(0, 0);
context.lineTo(100, 50);
context.lineWidth = 8;
context.strokeStyle = "pink";
// 描画路径
context.stroke();
context.lineTo(200, 0);
context.strokeStyle = "purple";
context.stroke();
想要的效果是画出两条不一样颜色的线,但是最后是一种颜色折线,这是因为我们只是用了一次beginPath
,所以就会把这两条线当成同一个路径,最后调用的stroke
就会把原本是粉色的线再用紫色画一遍,所以最终的效果就是只有一条折线。
所以需要使用beginPath
创建新路径,新的路径还是会有没有设置moveTo
时,这个位置并不是(0, 0),而是空的问题,所以需要使用moveTo
设置位置
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
// 创建路径
context.beginPath();
context.moveTo(0, 0);
context.lineTo(100, 50);
context.lineWidth = 8;
context.strokeStyle = "pink";
context.stroke();
context.beginPath();
// 创建新的路径,需要重新设置位置
context.moveTo(100, 50);
context.lineTo(200, 0);
context.strokeStyle = "purple";
context.stroke();
}
closePath 的作用
有可能会陷进closePath
是结束路径的误区,认为closePath
就是beginPath
的配套。但是closePath
和beginPath
并不是配套的,它们的功能不一样。所以closePath
之后的路径也不是新的路径,只有beginPath
才行。
而closePath
的作用是将最近绘制的路径闭合,和之前有没有beginPath
无关。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
// context.beginPath(); // 有无`beginPath`都没有影响
context.moveTo(10, 10);
context.lineTo(100, 50);
context.lineTo(20, 70);
context.closePath();
context.lineWidth = 8;
context.strokeStyle = "pink";
context.stroke();
}
上面我们只绘制了两条线,但是最终得到的结果是一个三角形,这是因为我们使用closePath
把最近绘制的路径闭合了。
绘制文本有两种方法。
fillText
:使用fillStyle
属性绘制文本strokeText
:使用strokeStyle
属性绘制文本
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
context.moveTo(10, 10);
context.lineTo(150, 75);
context.lineTo(30, 100);
context.closePath();
context.lineWidth = 1;
context.strokeStyle = "pink";
context.fillStyle = "purple";
context.fillText("CLZ", 50, 60);
context.strokeText("CLZ", 50, 80);
context.stroke();
}
可以通过font
、textAlign
、textBaseline
属性设置文本的字体、对齐方式、基线。
context.font = "700 16px Arial";
textAlign
:
- 如果是
start
,那么 x 坐标就是文本的左侧坐标 - 如果是
center
,那么 x 坐标就是文本的中心点坐标 - 如果是
end
,那么 x 坐标就是文本的右侧坐标
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
context.moveTo(10, 10);
context.lineTo(150, 75);
context.lineTo(30, 100);
context.closePath();
context.lineWidth = 1;
context.strokeStyle = "pink";
context.font = "700 16px Arial";
context.fillStyle = "purple";
context.textAlign = "start";
context.strokeText("CLZ", 50, 50);
context.textAlign = "center";
context.fillText("CLZ", 50, 65);
context.textAlign = "end";
context.strokeText("CLZ", 50, 80);
context.stroke();
}
textBaseline
类似
2D 换图上下文支持所有常见的绘制变化。rotate(a)
:围绕原点把图像旋转 a 弧度scale(x, y)
:缩放图像translate(x, y)
:移动原点
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
// 创建路径
context.beginPath();
// 绘制圆弧,参数分别是圆心x坐标、圆形y坐标、圆弧半径、圆弧起始点(单位:弧度)、圆弧终点(单位:弧度)、绘制方向(false为顺时针绘制,true为逆时针绘制)
context.arc(100, 100, 50, 0, 2 * Math.PI, true);
context.lineWidth = "8";
context.strokeStyle = "pink";
// 移动原点
context.translate(100, 100);
// 旋转
context.rotate(Math.PI);
// 缩放
context.scale(0.75, 0.75);
// 因为已经移动过原点了,所以这时候(0, 0)就是圆心
context.moveTo(0, 0);
context.lineTo(25, 30);
context.stroke();
}
上面的例子中,已经把很多变化都使用上了,如果想要了解具体例子可以注释掉其他部分。
save 和 restore 的作用
save
方法可以保存应用到绘图上下文的设置和变换,不保存绘图上下文的内容。后续可以通过restore
方法,恢复上下文的设置和变换。save
和restore
的使用类似于栈,后进先出。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
context.fillStyle = "red";
context.save();
context.fillStyle = "blue";
context.translate(100, 100);
context.save();
context.fillStyle = "purple";
context.translate(-100, -100);
context.fillRect(0, 0, 100, 100);
context.restore();
context.fillRect(0, 0, 100, 100);
context.restore();
context.fillRect(100, 0, 100, 100);
context.restore();
context.fillRect(0, 100, 100, 100);
}
分析:设 XXX 为绘图上下文的设置和变化
- 设置填充色为红色,
save
保存 - 设置填充色为蓝色,移动原点,
save
保存 - 设置填充色为紫色,移动原点,画出紫色的矩形
restore
恢复XXX,此时,原点为(100, 100),填充色为蓝色。画出蓝色的矩形restore
恢复**XXX**,此时,原点为(0, 0),填充色为红色。画出红色的矩形restore
已经没有保存的XXX,所以XXX不会变化
<img src="./avatar.png" alt="">
<canvas id="mycanvas" width="200" height="200">haha</canvas>
通过drawImage
把 HTML 的 img 元素或另一个 canvas 元素绘制到当前画布中。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
// 获取图像
const img = document.images[0];
// 在画布的坐标出绘制图像,此时图像和原来的图像一样大,指的是原文件的大小
// context.drawImage(img, 10, 10)
// 传入另外两个参数,设置绘制图像的宽高
context.drawImage(img, 10, 10, 100, 100);
}
只传3个参数,画到画布上的跟原来的图像一样大,但画布没那么大。所以会只有一部分。
传入五个参数,可以让设置图像的宽高,显示完整的图像。
去掉DOM树上的img
上面的做法是需要html
中有img
元素才能执行的.实际上,我们也可以通过image
对象来实现。
即获取图像不再是通过document.images[0]
,而是
const img = new Image();
img.src = "./avatar.png";
另外,绘制图像应该在img
的load
事件回调中调用。
const img = new Image();
img.src = "./avatar.png";
img.onload = () => {
// 传入另外两个参数,设置绘制图像的宽高
context.drawImage(img, 10, 10, 100, 100);
};
还可以接收 9 个参数,实现把原始图像的一部分绘制到画布上。
如:context.drawImage(img, 0, 10, 50, 50, 0, 100, 20, 30)
,从原始图像的(0, 10)开始,50 像素宽、50 像素高,画到画布上(0, 100)开始,宽 40 像素、高 60 像素。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
// 获取图像
const img = document.images[0];
// // 9个参数
context.drawImage(img, 0, 10, 300, 300, 100, 100, 40, 40);
}
操作的结果可以使用canvas.toDataURL()
方法获取。
再搭配下载图片的方式就能实现下载图片。(这里用的是a
标签方法)
const a = document.createElement("a");
a.href = mycanvas.toDataURL();
// 获取源图片的名字
a.download = img.src.split("/")[img.src.split("/").length - 1];
a.click();
设置好阴影有关的属性值,就能够自动为要绘制的形状或路径生成阴影
shadowOffsetX
:阴影相对于形状或路径的 x 坐标偏移。默认为 0shadowOffsetY
:阴影相对于形状或路径的 y 坐标偏移。默认为 0shadowBlur
:阴影的模糊量。默认值为 0,表示不模糊shadowColor
:阴影的颜色。默认为黑色
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
context.shadowOffsetX = 5;
context.shadowOffsetY = 10;
context.shadowBlur = 5;
context.shadowColor = "rgba(0, 0, 0, .2)";
context.fillStyle = "red";
context.fillRect(0, 0, 50, 50);
context.moveTo(100, 100);
context.lineTo(180, 20);
context.lineWidth = 12;
context.stroke();
}
线性渐变可以调用上下文的createLinearGradient
方法,接收四个参数:起点 x 坐标、起点 y 坐标、终点 x 坐标、终点 y 坐标,创建CanvasGradient
对象。
有了渐变对象后,就需要添加渐变色标了,通过addColorStop
可以添加色标,第一个参数范围为 0~1,第二个参数是 CSS 颜色字符串。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
const gradient = context.createLinearGradient(10, 10, 180, 180);
gradient.addColorStop(0, "red");
gradient.addColorStop(0.5, "blue");
gradient.addColorStop(1, "purple");
context.fillStyle = gradient;
context.fillRect(0, 0, 200, 200);
}
为了让渐变覆盖整个矩形,渐变的坐标和矩形的坐标应该搭配合适,不然只会显示部分渐变。
还可以调用上下文的createRadialGradient
方法来创建径向渐变。接收 6 个参数,前 3 个参数指定起点圆形中心的 x 坐标、y 坐标和半径,后 3 个参数指定终点圆形中心的 x 坐标和半径。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
let gradient = context.createRadialGradient(100, 100, 20, 100, 100, 80);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
context.fillStyle = gradient;
context.fillRect(0, 0, 200, 200);
}
上面这个渐变,简单理解就是内层圆为半径为 20 像素的纯白圆
,外层圆为 80 像素的白渐变黑圆
,剩余部分就是黑色。
图案适用于填充和描画图形的重复图像。
通过createPattern
方法,该方法接收两个参数,第一个参数是img
元素,第二个参数是是否重复,和background-repeat
属性一样。
然后,像渐变一样,把pattern
对象赋值给fillStyle
属性即可。
这个图案实际上就有点背景图像的味道了,通过创建pattern
对象,来控制图像的重复。然后,给绘图上下文的fillStyle
赋值,设置填充样式,最后再通过fillRect
来设置图案的位置和大小。
const mycanvas = document.getElementById("mycanvas");
// 确保浏览器支持canvas
if (mycanvas.getContext) {
const context = mycanvas.getContext("2d");
const image = document.images[0];
const pattern = context.createPattern(image, "repeat");
// const pattern = context.createPattern(image, 'repeat-y')
// const pattern = context.createPattern(image, 'no-repeat')
context.fillStyle = pattern;
context.fillRect(0, 0, 190, 190);
}
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK