js红宝书笔记十三 Canvas绘图

本文继续对JavaScript高级程序设计第四版 第十八章第3节 进行学习

1.填充和描边

2D 上下文有两个基本绘制操作:填充和描边。填充以指定样式(颜色、渐变或图像)自动填充形状,而描边只为图形边界着色。大多数 2D 上下文操作有填充和描边的变体,显示效果取决于两个属性:fillStyle 和 strokeStyle。

这两个属性可以是字符串、渐变对象或图案对象,默认值都为"#000000"。字符串表示颜色值,可以是 CSS 支持的任意格式:名称、十六进制代码、rgb、rgba、hsl 或 hsla。比如:

let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas>
if (drawing.getContext) {
 let context = drawing.getContext("2d");
 context.strokeStyle = "red";
 context.fillStyle = "#0000ff";
}

这里把 strokeStyle 设置为"red"(CSS 颜色名称),把 fillStyle 设置为"#0000ff"(蓝色)。

所有与描边和填充相关的操作都会使用这两种样式,除非再次修改。这两个属性也可以是渐变或图案,本章后面会讨论。

2.绘制矩形

矩形是唯一一个可以直接在 2D 绘图上下文中绘制的形状。与绘制矩形相关的方法有 3 个:fillRect()、strokeRect()和 clearRect()。这些方法都接收 4 个参数:矩形 x 坐标、矩形 y 坐标、矩形宽度和矩形高度。这几个参数的单位都是像素。

fillRect()方法用于以指定颜色在画布上绘制并填充矩形。填充的颜色使用 fillStyle 属性指定。来看下面的例子:

let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas>
if (drawing.getContext) {
 let context = drawing.getContext("2d");
 /*
 * 引自 MDN 文档
 */
 // 绘制红色矩形
 context.fillStyle = "#ff0000";
 context.fillRect(10, 10, 50, 50);
 // 绘制半透明蓝色矩形
 context.fillStyle = "rgba(0,0,255,0.5)";
 context.fillRect(30, 30, 50, 50);
}

以上代码先将 fillStyle 设置为红色并在坐标点(10, 10)绘制了一个宽高均为 50 像素的矩形。接着,使用 rgba()格式将 fillStyle 设置为半透明蓝色,并绘制了另一个与第一个部分重叠的矩形。结果就是可以透过蓝色矩形看到红色矩形。

strokeRect()方法使用通过 strokeStyle 属性指定的颜色绘制矩形轮廓。下面是一个例子:

let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas>
if (drawing.getContext) {
 let context = drawing.getContext("2d");
 /*
 * 引自 MDN 文档
 */
 // 绘制红色轮廓的矩形
 context.strokeStyle = "#ff0000";
 context.strokeRect(10, 10, 50, 50);
 // 绘制半透明蓝色轮廓的矩形
 context.strokeStyle = "rgba(0,0,255,0.5)";
 context.strokeRect(30, 30, 50, 50);
}

以上代码同样绘制了两个重叠的矩形,不过只有轮廓,而不是实心的

使用 clearRect()方法可以擦除画布中某个区域。该方法用于把绘图上下文中的某个区域变透明。
通过先绘制形状再擦除指定区域,可以创建出有趣的效果,比如从已有矩形中开个孔。

3.绘制路径

2D 绘图上下文支持很多在画布上绘制路径的方法。通过路径可以创建复杂的形状和线条。要绘制路径,必须首先调用 beginPath()方法以表示要开始绘制新路径。然后,再调用下列方法来绘制路径。

  • arc(x, y, radius, startAngle, endAngle, counterclockwise):
  • arcTo(x1, y1, x2, y2, radius):
  • bezierCurveTo(c1x, c1y, c2x, c2y, x, y)
  • lineTo(x, y):
  • moveTo(x, y):
  • quadraticCurveTo(cx, cy, x, y):
  • rect(x, y, width, height):以给定宽度和高度在坐标点(x, y)绘制一个矩形。这个方法与 strokeRect()和 fillRect()的区别在于,它创建的是一条路径,而不是独立的图形。

创建路径之后,可以使用 closePath()方法绘制一条返回起点的线。

以下参考Canvas中beginPath的重要性

var ctx  = document. getElementById ( 'cvs' ). getContext ( '2d' ) ; 
ctx. beginPath ( ) ; 
ctx. moveTo ( 100.5 , 20.5 ) ; 
ctx. lineTo ( 200.5 , 20.5 ) ; 
//stroke() 方法会实际地绘制出通过 moveTo() 和 lineTo() 方法定义的路径。默认颜色是黑色。
ctx. stroke ( ) ; 
ctx. moveTo ( 100.5 , 40.5 ) ; 
ctx. lineTo ( 200.5 , 40.5 ) 
ctx. strokeStyle  =  '#f00' ; 
ctx. stroke ( ) ;

上面的代码会得到什么样的图形呢?是不是一条黑线一条红线呢?从代码上看,我们的逻辑毫无问题,但结果是我们得到的是两条红线,并不是一黑一红。如果你明白这是为什么,那后面的你就不用看了。这就是beginPath的重要性。

canvas中的绘制方法(如stroke,fill),都会以“上一次beginPath”之后的所有路径为基础进行绘制。比如上面的代码里面我stroke了两次,其实这两次都是以第一次beginPath后的所有路径为基础画的。也就是说第一条路径我们stroke了两下,第一下是黑的,第二下是红的,所以最终也是红的。

  • 不管你用moveTo把画笔移动到哪里,只要不beginPath,那你一直都是在画一条路径。
  • fillRect与strokeRect这种直接画出独立区域的函数,也不会打断当前的path.

说到beginPath,就不得不提到closePath,两者是不是有很“紧”的联系呢?答案是几乎没有关系。closePath的意思不是结束路径,而是关闭路径,它会试图从当前路径的终点连一条路径到起点,让整个路径闭合起来。但是,这并不意味着它之后的路径就是新路径了!

我们在上面的代码的第一个lineTo后面加上closePath,可以发现还是得到了两条红线。但如果我们在第一个stroke后面加上beginPath,则会如愿得到一条黑线一条红线。

ctx. stroke ( ) ; 
ctx. beginPath ( ) ;  //注意啦! 
ctx. moveTo ( 100.5 , 40.5 ) ; 
ctx. lineTo ( 200.5 , 40.5 ) 
ctx. strokeStyle  =  '#f00' ; 
ctx. stroke ( ) ; 

总而言之,就是不要企图通过闭合现有路径来开始一条新路径,而开始一条新路径,以前的路径也不会闭合。

4.绘制文本

fillText()方法使用fillStyle 属性绘制文本,而 strokeText()方法使用 strokeStyle 属性。通常,fillText()方法是使用最多的,因为它模拟了在网页中渲染文本。例如,下面的例子会在前一节示例的表盘顶部绘制数字“12”:

context.font = "bold 14px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText("12", 100, 20); 
5.变换

上下文变换可以操作绘制在画布上的图像。2D 绘图上下文支持所有常见的绘制变换。在创建绘制上下文时,会以默认值初始化变换矩阵,从而让绘制操作如实应用到绘制结果上。对绘制上下文应用变换,可以导致以不同的变换矩阵应用绘制操作,从而产生不同的结果。以下方法可用于改变绘制上下文的变换矩阵。

  • 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()。

变换可以简单,也可以复杂。例如,在前面绘制表盘的例子中,如果把坐标原点移动到表盘中心,那再绘制表针就非常简单了:

let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas>
if (drawing.getContext) {
 let 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.translate(100, 100);
 // 绘制分针
 context.moveTo(0, 0);
 context.lineTo(0, -85);
 // 绘制时针
 context.moveTo(0, 0);
 context.lineTo(-65, 0);
 // 描画路径
 context.stroke();
} 
6.save/restore

参考canvas中的save和restore方法的作用

save方法可以理解为暂存当前画笔的状态,接下来对画笔的操作都不会被保存下来,直到restore方法被调用。讲得通俗一点,就是说,调用save方法,就是把当前的笔放笔架上,换一支笔,调用restore方法时,再把刚才放到笔架上的笔再拿下来用。

7.绘制图像
let image = document.images[0];
context.drawImage(image, 10, 10); 

可以参考h5 canvas ImageData

8.绘制曲线

参考图形系统开发实战课程:基础篇——4.绘制曲线和路径

  • beginPath 新建一条路径
  • closePath 将路径移动到起始点
  • moveTo 将路径移动到指定的坐标(x, y)
  • lineTo 添加直线至路径
  • rect 添加矩形至路径
  • arc 添加圆形至路径
  • ellipse 添加椭圆至路径
  • bezierCurveTo 添加三次贝塞尔曲线至路径
  • quadraticCurveTo 添加二次贝塞尔曲线至路径
  • stroke 通过线条来绘制图形轮廓
  • fill 通过填充路径的内容区域生成实心的图形
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • 元素负责在页面中设定一个区域,然后就可以通过 JavaScript 动态地在这个区域中绘制图形。 一、基本用法 要...
    LemonnYan阅读 18,547评论 0 7
  • 给大家简单介绍一下canvas在JS的用法及属性(如有介绍不到位 , 恳求各位大佬担待); 目录 一、Canvas...
    电子键盘手阅读 11,885评论 0 3
  • Canvas绘图 HTML5 的 canvas 元素使用 JavaScript 在网页上绘制图像。 画布是一个矩形...
    shanruopeng阅读 18,262评论 2 14
  • 使用 要使用 元素,必须先设置其width和height属性,指定可以绘图的区域大小。开始和结束标签中的内容是后备...
    Upcccz阅读 334评论 0 1
  •   HTML5 添加的最受欢迎的功能就是 元素。这个元素负责在页面中设定一个区域,然后就可以通过 JavaScri...
    霜天晓阅读 2,987评论 0 2