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 版)

评论