Canvas基础API使用教程
背景
canvas 应用非常广泛,可以做游戏引擎、表格文档、编辑器、图表、画板等。从基础 canvas API 入手,结合一定的算法、函数,可以绘制出任何你想要的效果,构建出更炫酷的应用。
绘制矩形
基础 API
- fillStyle
- fillRect
- strokeStyle
- strokeRect
- lineWidth
- lineCap
- lineJoin
- clearRect
- toDataURL
代码
以下展示绘制两个矩形核心代码
const canvas = document.getElementById("canvas");
// 确认浏览器智慧城canvas元素
if (canvas.getContext) {
// 获取到canvas上下文
const context = canvas.getContext("2d");
// 设置填充为红色
// 可以使用css任何颜色值:颜色名、十六进制码、rgb、rgba、hsl、hsla
context.fillStyle = "#ff0000";
// 绘制正方形,用上一步设置的红色填充
// x坐标10,y坐标10,宽度80,高度80
context.fillRect(10, 10, 80, 80);
// 设置描边为绿色
// 可以使用css任何颜色值:颜色名、十六进制码、rgb、rgba、hsl、hsla
context.strokeStyle = "green";
// 设置描边宽度,任意的整数
context.lineWidth = 10;
// 绘制正方形,用上一步设置的绿色描边
// x坐标10,y坐标10,宽度80,高度80
context.strokeRect(10, 10, 80, 80);
// 设置填充为蓝色半透明
context.fillStyle = "rgba(0,0,255,0.5)";
// 绘制矩形,用上一步设置的蓝色半透明填充
context.fillRect(50, 50, 80, 100);
// 设置线条末端样式
/**
* 平头:butt
* 圆头:round
* 方头:square
* */
context.lineCap = "square";
// 设置线条相交的方式,默认是斜接
/**
* 圆交:round
* 斜交:bevel
* 斜接:miter
* */
context.lineJoin = "round";
// 设置描边为蓝色
context.strokeStyle = "blue";
// 绘制矩形,用上一步设置的蓝色描边
context.strokeRect(50, 50, 80, 100);
// 清除画布上的矩形区域
// x坐标100,y坐标100,宽度20,高度20
context.clearRect(100, 100, 20, 20);
// toDataURL 将 canvas 转化为base64字符串格式的图片地址
const imgURI = canvas.toDataURL("image/png");
const image = document.createElement("img");
image.src = imgURI;
document.body.appendChild(image);
}
Demo
详细的执行请查看下方 demo
绘制路径
基础 API
arc(x,y,radius,startAngle,endAngle,counterclockwise)
以(x,y)为圆心绘制一条弧线,弧线半径为 radius,起始和结束角度(以弧度表示)分别为 startAngle 和 endAngle。最后一个参数表示 startAngle 和 endAngle 是否按照逆时针方向计算,值为 false 表示按顺时针方向计算。
arcTo(x1,y1,x2,y2,radius)
从上一点开始绘制一条弧线,到(x2,y2)为止,并且以给定半径 radius 穿过(x1,y1)
bezierCurveTo(c1x,c1y,c2x,c2y,x,y)
从上一点开始绘制一条曲线,到(x,y)为止,并且以(c1x,c1y)和(c2x,c2y)为控制点
lineTo(x,y)
从上一点开始绘制一条直线,到(x,y)为止
moveTo(x,y)
将绘图游标移动到(x,y),不画线
quadraticCurveTo(cx,cy,x,y)
从上一点开始绘制一条二次曲线,到(x,y)为止,并且以(cx,cy)作为控制点
rect(x,y,width,height)
从点(x,y)开始绘制一个矩形,宽度和高度分别由 width 和 height 指定
isPointInPath(x,y)
接受 x 和 y 坐标作为参数,用于在路径被关闭之前确定画布上的某一点是否位于路径上
代码
以下展示绘制一个不带数字的时钟表盘
const drawing = document.getElementById("canvas");
if (drawing.getContext) {
const context = drawing.getContext("2d");
// 开始路径
context.beginPath();
// 绘制外圆
context.arc(100, 100, 99, 0, 2 * Math.PI, false);
// 绘制内圆
context.moveTo(194, 100);
context.arc(100, 100, 94, 0, 2 * Math.PI, false);
// 绘制分针
context.moveTo(100, 100);
context.lineTo(100, 15);
// 绘制时针
context.moveTo(100, 100);
context.lineTo(35, 100);
// 描边路径
context.stroke();
}
Demo
详细的执行请查看下方 demo
绘制文本
基础 API
fillText(text, x, y,maxWidth)
要绘制的文本字符串 text,x 坐标,y 坐标,可选的最大像素宽度 maxWidth
strokeText(text, x, y,maxWidth)
要绘制的文本字符串 text,x 坐标,y 坐标,可选的最大像素宽度 maxWidth
measureText(text) 要绘制的文本 text,用于辅助确定文本的大小,返回一个 TextMetrics 对象,这个对象目前只有一个 width 属性
这三个 API 都以属性 font、textAlign、textBaseline 为基础
- font:表示文本样式、大小及字体,用 CSS 中指定字体的格式来指定,例如"10px Arial"。
- textAlign:表示文本对齐方式。可能的值有"start"、"end"、"left"、"right"和"center"。建议使用"start"和"end",不用使用"left"和"right",因为前两者的意思更稳妥,能同时适合从左到右和从右到左显示(阅读)的语言。
- textBaseline:表示文本的基线。可能的值有"top"、"hanging"、"middle"、"alphabetic"、"ideographic"、"bottom"。
代码
以下展示绘制一个带数字的时钟表盘
const drawing = document.getElementById("canvas");
if (drawing.getContext) {
// 省略 绘制表盘代码
// 在表盘上显示一个数字12
context.font = "bold 14px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText("12", 100, 20);
var text = context.measureText("foo"); // TextMetrics object
console.log(text.width); // 21.765625;
}
Demo
详细的执行请查看下方 demo
变换
基础 API
rotate(angle)
围绕原点旋转图像 angle 弧度。
scale(scaleX,scaleY)
缩放图像,在 x 方向乘以 scaleX,在 y 方向乘以 scaleY。scaleX 的 scaleY 的默认值都是 1.0。
translate(x,y)
将坐标原点移动到(x,y)。执行这个变换后,坐标(0,0)会变成之前由(x,y)表示的点。
transform(m1_1,m1_2,m2_1,m2_2,dx,dy)
直接修改变换矩阵,方式是乘以如下矩阵
m1_1 m1_2 dx m2_1 m2_2 dy 0 0 1
setTransform(m1_1,m1_2,m2_1,m2_2,dx,dy)
将变换矩阵重置为默认状态,然后再调用 transform()。
代码
以下展示使用变换原点绘制一个时钟表盘
const drawing = document.getElementById("canvas");
if (drawing.getContext) {
const context = drawing.getContext("2d");
// 开始路径
context.beginPath();
// 绘制外圆
context.arc(100, 100, 99, 0, 2 * Math.PI, false);
// 绘制内圆
context.moveTo(194, 100);
context.arc(100, 100, 94, 0, 2 * Math.PI, false);
// 变换原点,所有的数学计算都基于(0,0)而不是(100,100),使得绘制分针和时针的计算变得简单
context.translate(100, 100);
// 旋转表针
context.rotate(1);
// 绘制分针
context.moveTo(0, 0);
context.lineTo(0, -85);
// 绘制时针
context.moveTo(0, 0);
context.lineTo(-65, 0);
// 描边路径
context.stroke();
}
Demo
详细的执行请查看下方 demo
上下文状态
基础 API
rotate、scale、translate、transform、fillStyle、strokeStyle 等属性设置,会在当前上下文中一直有效,除非再对上下文进行什么修改,可以用 save 和 restore 来跟踪上下文的设置变化。
save()
如果你知道将来还要返回某组属性与变换的组合,可以调用 save()方法。调用这个方法后,当时所有设置都会进入一个栈结构,得以妥善保管。然后可以对上下文进行其他修改。
restore() 如果想要回到之前保存的设置时,可以调用 restore()方法,在保存设置的栈结构中向前返回一级,恢复之前的状态。连续调用 save()可以把更多设置保存到栈结构中,之后再连续调用 restore()则可以一级一级返回。
注意,save()方法保存的只是对绘图上下文的设置和变换,不会保存绘图上下文的内容。
代码
以下展示 save 和 restore 的作用
const drawing = document.getElementById("canvas");
if (drawing.getContext) {
const context = drawing.getContext("2d");
context.fillStyle = "#ff0000";
context.save();
context.fillStyle = "#00ff00";
context.translate(100, 100);
context.save();
// 从点(100,100)开始绘制蓝色矩形
context.fillStyle = "#0000ff";
context.fillRect(0, 0, 100, 200);
// 从点(110,110)开始绘制绿色矩形
context.restore();
context.fillRect(10, 10, 100, 200);
// 从点(0,0)开始绘制红色矩形
context.restore();
context.fillRect(0, 0, 100, 200);
}
Demo
详细的执行请查看下方 demo
绘制图像
基础 API
有三种不同的组合
drawImage(image, x, y)
绘制到画布上的图像与原始大小一样。要绘制的图像 image(image 元素,canvas 元素),绘制目标图像的起点坐标 x 、坐标 y
drawImage(image, x, y, tw, th)
缩放绘制的图像。要绘制的图像 image,绘制目标图像的起点坐标 x 、坐标 y、宽度 tw、高度 th
drawImage(image, ox, oy, ow, oh, x, y, tw, th) 裁剪要绘制的图像。要绘制的图像 image,裁剪源图像的坐标 ox、坐标 oy、宽度 ow、高度 oh,绘制目标图像的起点 x 坐标、y 坐标、宽度 tw、高度 th
此 API 操作的结果可以使用 canvas 对象的 toDataURL()方法获得图片,但是如果图像来自跨域网站,canvas 会受到污染,从而调用 toDataURL() 会报错。
代码
以下展示 drawImage 的使用
const drawing = document.getElementById("canvas");
if (drawing.getContext) {
const context = drawing.getContext("2d");
const img = document.createElement("IMG");
img.src =
"https://gcore.jsdelivr.net/gh/openHacking/static-files@main/uPic/OpenHack-mp.weixin.png";
img.onload = () => {
context.drawImage(img, 0, 0, 400, 150);
};
}
Demo
详细的执行请查看下方 demo
阴影
基础 API
2D 上下文会 context 根据以下属性自动为形状或路径绘制阴影
shadowColor
用 CSS 颜色格式表示的阴影颜色,默认为黑色
shadowOffsetX
形状或路径 x 轴方向的阴影偏移量,默认为 0
shadowOffsetY
形状或路径 y 轴方向的阴影偏移量,默认为 0
shadowBlur 模糊的像素数,默认 0,即不模糊
代码
以下展示如何使用阴影设置
const drawing = document.getElementById("canvas");
if (drawing.getContext) {
const context = drawing.getContext("2d");
// 设置阴影
context.shadowOffsetX = 5;
context.shadowOffsetY = 5;
context.shadowBlur = 4;
context.shadowColor = "rgba(0,0,0,0.5)";
// 绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
// 绘制蓝色矩形
context.fillStyle = "rgba(0,0,255,1)";
context.fillRect(30, 30, 50, 50);
}
Demo
详细的执行请查看下方 demo
渐变
基础 API
createLinearGradient(sx, sy, ex, ey)
创建一个线性渐变对象,可被赋值给 fillStyle 或者 strokeStyle 从而使用渐变来绘制形状或者描边。起点的坐标 sx、sy,终点的坐标 ex、ey
createRadialGradient(sx, sy, sr, ex, ey, er) 创建一个径向渐变(或放射渐变)对象。起点圆的原心坐标 sx、sy 和半径 sr,终点圆的原心坐标 ex、ey 和半径 er
addColorStop(position, color) 渐变对象的方法,用来指定色标。色标位置 position,CSS 颜色值 color
代码
以下展示渐变的使用
const drawing = document.getElementById("canvas");
if (drawing.getContext) {
const context = drawing.getContext("2d");
// 创建一个线性渐变对象
const gradient = createRectLinearGradient(context, 30, 30, 50, 50);
// 设置色标,开始位置白色,结束位置黑色
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
// 绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
// 绘制渐变矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);
}
function createRectLinearGradient(context, x, y, width, height) {
return context.createLinearGradient(x, y, x + width, y + height);
}
Demo
详细的执行请查看下方 demo
模式
基础 API
代码
Demo
图像数据
合成
总结
以上是在学习红宝书(JavaScript 高级程序设计)关于 Canvas 画布的基础上做的一点学习笔记,还有更深入的canvas内容后续会分享。
参考
- Canvas
- JavaScript 高级程序设计(第 3 版)
评论