JS高级--canvas
canvas
- 被译为帆布、画布、油布,可以利用 JS 在页面上绘制图像,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。
- 常被应用于:图形、创建动画、游戏、照片、可视化数据(数据图表化,百度的 echart)替代 flash 的大部分工作
- 使用 canvas 制作游戏的网站:phaserChina、
并不是所有浏览器都支持 canvas,测试:
<canvas width="500" height="500" style="border:1px solid">
您的浏览器不支持canvas元素,请升级您的浏览器
</canvas>
<canvas width = "500" height = "500" id = "cvs"></canvas>
-
-
在 js 中获取到 canvas 元素节点
-
获取到上下文创建 context 对象
-
使用 js 绘制
首先需要创建上下文 context 对象:
let cvs = document.getElementById("canvas");// 获取元素
let ctx = cvs.getContext("2d"); // 创建上下文 context 对象 便于理解也可以取名叫 pen
// 第一条线 ctx.moveTo(10, 10); // 落笔点 ctx.lineTo(50, 10); // 从落笔点画一条线(路径)到指定位置 // 第二条线 ctx.moveTo(10, 20); ctx.lineTo(50, 20); // 描绘 ctx.stroke(); // 通过路径列表描边
ctx.moveTo(20, 20); ctx.lineTo(100, 20); ctx.moveTo(20, 50); ctx.lineTo(100, 50); ctx.strokeStyle = "red"; ctx.stroke();
ctx.moveTo(10, 10); ctx.lineTo(50, 10); ctx.lineWidth = "10"; // 设置线段宽度 ctx.stroke();
线条交界处的样式使用 lineJoin
- miter [ˈmaitə] 直角 默认
- round 圆角
- bevel [ˈbevl] 斜切角
ctx.moveTo(100, 100); ctx.lineTo(200, 100); ctx.lineTo(50, 150); ctx.lineWidth = 10; ctx.lineJoin = "round"; ctx.stroke();
线帽,控制线条转折样式: 1. butt 默认值 2. square 方形 3. round
除了矩形以外,其他多边形都需要用一条一条的线段去组合。也就是说路径其实就是指的一条一条的线段。一个 canvas 中存在多个路径,称为 路径列表。
路径起点 moveTo(), 下一个点 lineTo(),连接 storke(),lineTo() 可以设置多个点,而storke() 只需要调用一次 。
// 绘制一个三角 ctx.moveTo(100, 100); ctx.lineTo(200, 100); ctx.lineTo(200, 200); ctx.lineTo(100, 100); // 回到原点 与 ctx.closePath() 作用相同 ctx.stroke(); // 绘制一个矩形 ctx.moveTo(50, 10); ctx.lineTo(100, 10); ctx.lineTo(100, 50); ctx.lineTo(50, 50); ctx.lineTo(50, 10); ctx.stroke();
对于上面的多个图形,如果只是 stroke() 倒也没发现什么不对,但是如果我们将 stroke() 变成 fill() 填充:
// 绘制一个三角 ctx.moveTo(100, 100); ctx.lineTo(200, 100); ctx.lineTo(200, 200); ctx.closePath(); ctx.stroke(); // 描边 // 绘制一个矩形 ctx.moveTo(50, 10); ctx.lineTo(100, 10); ctx.lineTo(100, 50); ctx.lineTo(50, 50); ctx.closePath(); ctx.fill(); // 改为填充 两个图都会被填充
这时问题就出现了,明明是想填充后面的矩形,但却连第一个三角形也一起填充了。
- 同一个画布中,每一次绘制的路径实际上都被保存在了栈空间,当最后通过 fill() 填充或 stroke() 描绘。 它会把整个内存空间中所有路径都算上。因此在每次开始绘制一个新的路径前,可以把内存空间保存的前面的图形路径清除。
如果将上面代码添加上接可以实现各自显示各自的样式了:
// 绘制一个三角 ctx.moveTo(100, 100); ctx.lineTo(200, 100); ctx.lineTo(200, 200); ctx.closePath(); ctx.stroke(); // 描边 描边的三角形 // 绘制一个矩形 ctx.beginPath(); // 清除路径列表 ctx.moveTo(50, 10); ctx.lineTo(100, 10); ctx.lineTo(100, 50); ctx.lineTo(50, 50); ctx.closePath(); ctx.fill(); // 填充 填充的矩形
建议: 在实际开发中为了避免每次都去区分哪个图形是先绘制的,哪个图形是后绘制的,一般会在每次图形之前都做一次清空。
// 绘制一个三角 ctx.moveTo(100, 100); ctx.lineTo(200, 100); ctx.lineTo(200, 200); ctx.strokeStyle = "red"; // 描边颜色 ctx.closePath(); ctx.stroke(); // 绘制一个矩形 ctx.beginPath(); ctx.moveTo(50, 10); ctx.lineTo(100, 10); ctx.lineTo(100, 50); ctx.lineTo(50, 50); ctx.fillStyle = "green"; // 填充颜色 ctx.closePath(); ctx.fill();
4.1 边框矩形 strokeRect() 和填充矩形 fillRect()
默认是黑色。不使用路径也不生成路径。只需要提供原点 x y 坐标,和宽高。
ctx.strokeRect(x, y, width, height); ctx.fillRect(x, y, width, height);
ctx.rect(x, y, width, height);
ctx.clearRect(x, y, width, height); // 清空画布 ctx.clearRect(0, 0, cvs.width, cvs.height);
// ctx.arc(圆心x, 圆心y, 半径, 起始弧度 3点钟位置为0度, 结束弧度, false顺时针 true 逆时针); ctx.arc(x, y, r, startAngle, endAngle, boolean);
角度与弧度的转换公式:
如果要绘制圆形,那么起始角度为 0,结束角度为 360 即:Math.PI / 180 *360 度 等同于 Math.PI* 2

ctx.arc(200, 200, 50, 0, Math.PI*2); ctx.stroke();
绘制一个半圆:
ctx.beginPath(); ctx.moveTo(400,300); ctx.arc(400, 300, 40, 0,Math.PI / 180 * 90, false); ctx.closePath(); ctx.stroke();
-
-
restore() 恢复之前保存的状态,以防止 save( ) 后对 canvas 执行的操作对后续的绘制有影响
现在有以下代码分析执行结果:
ctx.lineWidth = 3; ctx.strokeStyle = "red"; ctx.save(); ctx.lineWidth = 5; ctx.strokeStyle = "orange"; ctx.save(); ctx.lineWidth = 8; ctx.strokeStyle = "green"; ctx.save(); ctx.moveTo(10, 10); ctx.lineTo(100, 10); ctx.stroke();

样式始终会执行栈顶的样式。使用 restore() 则可以“取出”读取状态
为了简便编码减少错误,可以使用固定格式:
ctx.save(); // 保存之前的样式 ctx.beginPath(); // 开始绘制 // ...画图 ctx.restore(); // 取出之前的样式
7.1 移动 translate()
语法: ctx.translate(x,y)
该方法是移动的画布 (0, 0) 点,而不是图形。图形的 (0, 0) 点始终是相对于画布的 (0, 0) 点设置的。连续使用效果是叠加的。
ctx.save(); ctx.beginPath(); ctx.translate(100, 100); // 原点移动到 (100,100) 点 ctx.translate(100, 100); // 原点移动到 (200,200) 点 ctx.rect(0, 0, 100, 100); ctx.fill(); ctx.restore();
语法: ctx.rotate(deg)
-
deg: 元素顺时针旋转的角度,以弧度为单位 -
所有图形相对于画布 (0, 0) 点转,连续使用效果是叠加的。
-
想要更改图形的旋转点,就要结合
translate(x, y)更改画布的 (0,0) 点。
ctx.rotate(Math.PI / 180 * 45); // demo ctx.translate(200, 200); ctx.rotate(Math.PI / 180 * 45); ctx.fillRect(0, 0, 100, 100);

浙公网安备 33010602011771号