Canvas 02
canvas画布栅格
默认canvas画布就一个高150px,宽300px空间区域。 我们说的网格中一个单位就相当于一个像素,我们可以把他看成一个 二维空间坐标系,以左上角为原点,所有元素的位置都是相对于原点进行 定位。我们可以平移原点,旋转,缩放我们的canvas栅格。
canvas图像绘制
不同于SVG,canvas只支持两种形式的图形绘制:矩形和路径。
矩形绘制 fillRect(x,y,width,height) 绘制一个填充矩形 strokeRect(x,y,width,height) 绘制一个矩形边框 clearRect(x,y,width,height) 清除指定矩形区域,使清除部分完全透明。(类似橡皮擦)
x,y 相当我们指定相对原点位置绘制矩形,width、height就是为矩形绘制宽高
function draw() {
const canvas = document.getElementById("canvas");
if (canvas.getContext) {
const ctx = canvas.getContext("2d");
ctx.fillRect(25, 25, 100, 100);
ctx.clearRect(45, 45, 60, 60);
ctx.strokeRect(50, 50, 50, 50);
}
}
不同于路径函数,这三个矩形函数执行完之后,图像会立刻在canvas画布上生效。
绘制路径
路径就是一系列点的集合。图形的基本元素也是路径,路径绘制图形需要注意
- 创建路径起始点
- 使用命令画出路径
- 封闭路径
- 使用描边或者填充函数进行图形渲染
beginPath() —— 新建路径 closePath() —— 闭合路径 strock() —— 只绘制图形的轮廓 fill() —— 填充图形
路径本质上是由很多子路径构成的,我们可以认为一组子路径都是在一个列表中,由其构成我们的图形。beginPath调用列表就会清空,我们就可以绘制新的图形了。 closePath函数调用会自动绘制一条从当前点到起始点的直线来闭合图形。
这里我们需要注意的是fill调用时,会自动闭合图形,stroke函数调用不会自动闭合图形。
三角形绘制
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(75, 50);
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);
ctx.fill();
}
}
moveTo(x,y) —— 移动绘制点(画笔的移动) 笑脸绘制
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // 绘制大圆脸
ctx.moveTo(110, 75); // 鼠标移动到小嘴绘制右边点 避免出现多余线条
ctx.arc(75, 75, 35, 0, Math.PI, false); // 口 (顺时针)
ctx.moveTo(65, 65); //避免出现多余线条
ctx.arc(60, 65, 5, 0, Math.PI * 2, true); // 左眼
ctx.moveTo(95, 65);
ctx.arc(90, 65, 5, 0, Math.PI * 2, true); // 右眼
ctx.stroke();
}
}
lineTo(x,y) —— 直线绘制 起始点就是当前路径绘制的结束点,或者可以手动moveTo移动起始点
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
// 填充三角形
ctx.beginPath();
ctx.moveTo(25, 25);
ctx.lineTo(105, 25);
ctx.lineTo(25, 105);
ctx.fill();
// 描边三角形
ctx.beginPath();
ctx.moveTo(125, 125);
ctx.lineTo(125, 45);
ctx.lineTo(45, 125);
ctx.closePath();
ctx.stroke();
}
}
两种三角形绘制步骤有所不同,正是我们前面提到fill填充函数会自动调用closePath进行图形路径闭合,而stroke方式不会自动闭合,若没有调用closePath,则只会绘制两条线。
arc(x,y,radius,startAngle,endAngle,anticlockwise=false) —— 圆弧或圆的绘制 x y 为圆心 radius为半径 开始弧度和结束弧度(x轴为基准),最后一个参数为绘制圆弧方向,默认为顺时针。
arcTo(x1, y1, x2, y2, radius) —— 给定控制点和半径画一段圆弧,再以直线连接两个控制点
arc() 函数中表示角的单位是弧度,不是角度。角度与弧度的 js 表达式: 弧度=(Math.PI/180)*角度。
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 3; j++) {
ctx.beginPath();
var x = 25 + j * 50; // x 坐标值
var y = 25 + i * 50; // y 坐标值
var radius = 20; // 圆弧半径
var startAngle = 0; // 开始点
var endAngle = Math.PI + (Math.PI * j) / 2; // 结束点
var anticlockwise = i % 2 == 0 ? false : true; // 顺时针或逆时针
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
if (i > 1) {
ctx.fill();
} else {
ctx.stroke();
}
}
}
}
}
二次贝塞尔曲线以及三次贝塞尔曲线(以下都是介绍路径类型)
quadraticCurveTo(cp1x, cp1y, x, y) —— 二次贝塞尔曲线 cp1x,cp1y为控制点, x,y为结束点。 bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)—— 三次贝塞尔曲线 cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点。 二次贝塞尔曲线和三次贝塞尔曲线使用有一定难度,因为我们没法直接观察预测图形的变化。通过例子进行实战和熟悉。
这个例子使用多个二次贝塞尔曲线绘制对话气泡。
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
// 二次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(75, 25);
ctx.quadraticCurveTo(25, 25, 25, 62.5);
ctx.quadraticCurveTo(25, 100, 50, 100);
ctx.quadraticCurveTo(50, 120, 30, 125);
ctx.quadraticCurveTo(60, 120, 65, 100);
ctx.quadraticCurveTo(125, 100, 125, 62.5);
ctx.quadraticCurveTo(125, 25, 75, 25);
ctx.stroke();
}
}
使用三次贝塞尔曲线绘制心形
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
//三次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(75, 40);
ctx.bezierCurveTo(75, 37, 70, 25, 50, 25);
ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
ctx.bezierCurveTo(20, 80, 40, 102, 75, 120);
ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
ctx.fill();
}
}
矩形路径
rect(x, y, width, height) —— 相对原点坐标为x,y,对应宽高的矩形。
执行该方法时,自动调用moveTo方法将坐标相对参数设置为原点(0,0) 组合使用路径绘制例子
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
roundedRect(ctx, 12, 12, 150, 150, 15);
roundedRect(ctx, 19, 19, 150, 150, 9);
roundedRect(ctx, 53, 53, 49, 33, 10);
roundedRect(ctx, 53, 119, 49, 16, 6);
roundedRect(ctx, 135, 53, 49, 33, 10);
roundedRect(ctx, 135, 119, 25, 49, 10);
ctx.beginPath();
ctx.arc(37, 37, 13, Math.PI / 7, -Math.PI / 7, false);
ctx.lineTo(31, 37);
ctx.fill();
for (var i = 0; i < 8; i++) {
ctx.fillRect(51 + i * 16, 35, 4, 4);
}
for (i = 0; i < 6; i++) {
ctx.fillRect(115, 51 + i * 16, 4, 4);
}
for (i = 0; i < 8; i++) {
ctx.fillRect(51 + i * 16, 99, 4, 4);
}
ctx.beginPath();
ctx.moveTo(83, 116);
ctx.lineTo(83, 102);
ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
ctx.lineTo(111, 116);
ctx.lineTo(106.333, 111.333);
ctx.lineTo(101.666, 116);
ctx.lineTo(97, 111.333);
ctx.lineTo(92.333, 116);
ctx.lineTo(87.666, 111.333);
ctx.lineTo(83, 116);
ctx.fill();
ctx.fillStyle = "white";
ctx.beginPath();
ctx.moveTo(91, 96);
ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
ctx.moveTo(103, 96);
ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
ctx.fill();
ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
ctx.fill();
ctx.beginPath();
ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
ctx.fill();
}
}
// 封装的一个用于绘制圆角矩形的函数。
function roundedRect(ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x, y + radius);
ctx.lineTo(x, y + height - radius);
ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
ctx.lineTo(x + width - radius, y + height);
ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
ctx.lineTo(x + width, y + radius);
ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
ctx.lineTo(x + radius, y);
ctx.quadraticCurveTo(x, y, x, y + radius);
ctx.stroke();
}
fillStyle 属性 修改填充样式颜色
Path2D 对象
Path2D用于缓存或者记录绘画命令,可以快速回顾路径。 Path2D对象初始化。
new Path2D(); // 空的 Path 对象
new Path2D(path); // 克隆 Path 对象
new Path2D(d); // 从 SVG 建立 Path 对象
所有前面提到的路径方法都可以在Path2D对象上使用。moveTo,rect等。 Path2D.addPath(path [, transform]) —— 添加一条路径当前路径中
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
var rectangle = new Path2D();
rectangle.rect(10, 10, 50, 50);
var circle = new Path2D();
circle.moveTo(125, 35);
circle.arc(100, 35, 25, 0, 2 * Math.PI);
ctx.stroke(rectangle);
ctx.fill(circle);
}
}
随着新的 Path2D API 产生,几种方法也相应地被更新来使用 Path2D 对象而不是当前路径。带路径参数的stroke和fill可以把对象画在画布上。
使用SVG paths
当前Path2D api可以使用 SVG path data来初始化canvas上的路径。
例子挑战:这条路径将先移动到点 (M10 10) 然后再水平移动 80 个单位(h 80),然后下移 80 个单位 (v 80),接着左移 80 个单位 (h -80),再回到起点处 (z)。
new Path2D("M10 10 h 80 v 80 -80 z")
写在最后
未完待续… 下期讲解canvas中样式和色彩应用。 希望大家好好生活,健康快乐。