js前端开发—canvas篇—2D上下文(未完待续)

参考资料

《Javascript高级层序设计》(第3版)

前言

本人菜鸟,入IT只为当鼓励师。本编文章旨在总结《Javascript高级层序设计(第3版)》一书中第15章canvas的一些api。第(一)篇只总结2D上下文。

一、<canvan>元素

出处:HTML5。
用途:设定一个区域,通过JS动态地在区域上绘制图形。
组成:由几组API构成,可分为 2D上下文(基本绘图能力) 和 3D上下文(WebGL)。

兼容性

  • 浏览器:IE9+、Firefox1.5+、Safari2+、Opera9+、Chrome、IOS版Safari、Android版WebKit。
  • 操作系统:Windows XP因缺少必要的绘图程序,不管用什么浏览器都不支持。

二、基本用法

1. 建立区域

要使用<canvas>元素,必须设置width和height属性,指定可以绘图的区域大小。设置完后,如果浏览器兼容<canvas>标签,则建立的区域应是空白的;否则,会显示其中的文本:A drawing of something。

<canvas id="drawing" width="200" height="200">A drawing of something</canvas>

2. 取得绘图上下文(<canvas>元素的DOM对象)

要在这块画布上绘图,需要取得绘图上下文。绘图上下文也就是取得相应<canvas>元素的DOM对象。

// 取得DOM对象,也为canvas对象
var drawing = document.getElementById("drawing");

3. 取得2D上下文对象 getContext("2d")

只取得DOM对象还不行,还要调用<canvas>DOM对象拥有的 getContext(param) 方法。其中传入参数为一个字符串,因为本篇文章总结的是2D上下文(平面),故传入"2d",获得2D上下文对象。后面一切关于绘图的方法和属性,其实都是围绕着2D上下文对象开展的,也就是代码示例中的context

method param return
getContext(param) 上下文对象的类型,有如下选择:</br>"2d"(2d上下文)</br>"3d"(3d上下文) 上下文对象

在调用该方法之前,为了保证程序的健壮性,最好加入检测代码。

// 确定浏览器支持<canvas>元素
if(drawing.getContext) {
    // 取得2D上下文对象
    var context = drawing.getContext("2d");
    // 更多代码
}

4. 导出在<canvas>元素上绘制的图像

当在<canvas>元素上绘制完图像后,可以调用DOM对象的 toDataURL(param) 方法,将该图像导出成为一张图片,传入参数为欲导出图片的MIME类型,例如:image/pngimage/jpeg 等等。拥有该方法的对象是<canvas>元素的DOM对象,并不是2d上下文对象context,两者要区分开来

method param return compatibility
toDataURL(param) 图像的MIME类型格式,有如下选择:</br>"image/jpeg"(Firefox和Opera默认)</br>"image/png"(其他默认) 图像的数据URI IE9+、Firefox3.5+ 、Opera10
var drawing = document.getElementById("drawing");

// 确定浏览器支持<canvas>元素
if(drawing.getContext) {
    // 取得图像的数据URI
    var imgURI = drawing.toDataURI("image/png");
   
    // 显示图像
    var image = document.createElement("img");
    image.src = imgURI;
    document.body.appendChild(image);
}

三、2D上下文 context

(0, 0)为<canvas>元素所占区域的原点坐标,x向右递增,y向下递增

1. 填充和描边

attribute value effect
fillStyle String类型:颜色名、十六进制码、rgb等,如"#0000ff" 设置填充颜色
strokeStyle String类型:颜色名、十六进制码、rgb等,如"red" 设置描边颜色
var drawing = document.getElementById("drawing");

// 确定浏览器支持<canvas>元素
if(drawing.getContext) {
    var context = drawing.getContext("2d");
    // 以红色描边 
    context.strokeStyle = "red"; 
    // 以蓝色填充 
    context.fillStyle = "#0000ff"; 
    // 绘制描边矩形 
    context.strokeRect(10, 10, 50, 50); 
    // 绘制填充矩形 context.fillRect
    (65, 10, 50, 50);
}

2. 绘制矩形

2.1 填充矩形

method param effect
fillRect(param1,param2, param3,param4) param1:矩形的x坐标</br>param2:矩形的y坐标</br>param3:矩形的宽度</br>param4:矩形的高度 绘制填充矩形,默认为黑色,可给fillStyle赋值改变其填充颜色
// 填充
var drawing = document.getElementById("drawing");

// 确定浏览器支持<canvas>元素
if(drawing.getContext) { 
    var context = drawing.getContext("2d");
    // 以红色填充
    context.fillStyle = "#ff0000";
    // 绘制填充矩形
    context.fillRect(10, 10, 50, 50);
    // 以半透明的蓝色填充
    context.fillStyle = "rgba(0, 0, 255, 0.5)";
    // 绘制填充矩形
    context.fillRect(30, 30, 50, 50);
 }

2.2 描边矩形

method param effect
strokeRect(param1, param2, param3, param4) param1:矩形的x坐标</br>param2:矩形的y坐标</br>param3:矩形的宽度</br>param4:矩形的高度 绘制描边矩形,默认为黑色,可给strokeStyle赋值改变其边框颜色
// 描边
var drawing = document.getElementById("drawing");

// 确定浏览器支持<canvas>元素
if(drawing.getContext) {
    var context = drawing.getContext("2d");
    // 以红色描边 
    context.strokeStyle = "#ff0000"; 
    // 绘制描边矩形 
    context.strokeRect(10, 10, 50, 50); 
    // 以蓝色描边 
    context.strokeStyle = "rgba(0, 0, 255, 0.5)"; 
    // 绘制描边矩形 context.strokeRect(30, 30, 50, 50); 
}

2.3 清除矩形

method param effect
clearRect(param1, param2, param3, param4) param1:矩形的x坐标</br>param2:矩形的y坐标</br>param3:矩形的宽度</br>param4:矩形的高度 清除一块矩形区域
// 清除矩形区域 
var drawing = document.getElementById("drawing");  

// 确定浏览器支持<canvas>元素 
if(drawing.getContext) { 
      var context = drawing.getContext("2d"); 
      // 绘制红色矩形 
      context.fillStyle = "#ff0000"; 
      context.fillRect(10, 10, 50, 50);

      // 绘制半透明的蓝色矩形 
      context.fillStyle = "rgba(0, 0, 255, 0.5)"; 
      context.fillRect(30, 30, 50, 50); 

      // 在两个矩形重叠的地方清楚一个小矩形 
      context.clearRect(40, 40, 10, 10); 
}

3. 绘制路径

3.1 创建路径

method param effect
arc(x,y,radius,startAngle,counterclockwise) (x, y):圆心的坐标</br>radius:弧线半径</br>startAngle:起始角度</br>endAngle:结束角度</br>counterclockwise:角度的方向(true:逆时针方向,false:顺时针方向 给定圆心(x,y)和半径radius、起始和结束的角度、方向,绘制一条弧线
arcTo(x1,y1,x2,y2,radius) (x1, y1):需穿过的点的坐标</br>(x2, y2):终点坐标</br>radius:以给定的半径穿过(x1, y1) 从上一点绘制一条弧线,到(x2,y2)为止,并且以给定的半径穿过(x1, y1)
bezierCurveTo(c1x,c1y,c2x,c2y,x,y) (c1x,c1y):控制点</br>(c2x,c2y):另一个控制点</br>(x, y):终点坐标 从上一点绘制一条弧线,到(x,y)为止,并且以(c1x,c1y)和(c2x,c2y)为控制点
lineTo(x,y) (x,y):终点坐标 从上一点开始绘制一条直线,到(x,y)为止
moveTo(x,y) (x,y):绘图游标移动到的点的坐标 将绘图游标移动到(x,y),不画线
quadraticCurveTo(cx,cy,x,y) (cx,cy):控制点</br>(x,y):终点坐标 从上一点开始绘制一条二次曲线,到(x,y)为止,并且以(cx,cy)为控制点
rect(x,y,width,height) (x,y):起点坐标</br>width:矩形的宽</br>height:矩形的高 从点(x,y)开始绘制一个矩形,宽高分别为width和height。这个方法绘制的是矩形路径,而不是strokeRect()和fillRect()所绘制的独立形状

3.2 处理路径

method param effect
beginPath() null 开始创建路径
closePath() null 关闭路径,回到起点
fill() null 用fillStyle来填充路径
stroke() null 用strokeStyle来对路径描边
clip() null 在路径上创建一个剪切区域

3.2.1 用上面某些方法描绘一个时钟

 var drawing = document.getElementById("drawing");  

// 确定浏览器支持<canvas>元素 
if(drawing.getContext) { 
    var 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(); 
}

3.2.2 用fill()方法填充路径

var drawing = document.getElementById("drawing");

// 确定浏览器支持<canvas>元素 
if(drawing.getContext) {  
    var context = drawing.getContext("2d");  

    // 开始路径 
    context.beginPath();  

    // 绘制圆 
    context.arc(100, 100, 99, 0, 2*Math.PI, false);  

    // 填充路径 
    context.fill();
}

3.2.3 当路径不闭合时fill()方法无效

var drawing = document.getElementById("drawing"); 

// 确定浏览器支持<canvas>元素 
if(drawing.getContext) { 
    var context = drawing.getContext("2d"); 

    // 绘制一条对角线 
    context.moveTo(0, 0); 
    context.lineTo(200, 200); 

    // 填充路径 
    context.fill(); 
}

3.3 判断路径点

method param effect
isPointInPath(x,y) (x,y):某一任意点的坐标 在路径被关闭前确定判断画布上的点(x,y)是否在路径上

4. 绘制文本

4.1 2d上下文的文本属性

attribute value effect
font String类型,如"bold 14px Arial" 字体粗细、大小和样式
textAlign String类型,有如下选择:</br>"start"(x坐标表示文本左端),"center"(正中),"end"(右端) 文本的水平对齐方式
textBaseline String类型,有如下选择:</br>"top"(y坐标表示文本顶端),"middle"(正中),"bottom"(底端),"hanging"、"alphabetic"、"ideographic"(y坐标指向特定基线坐标) 文本的垂直对齐方式

其实,fillText()和strokeText()方法都可以接受第四个参数,表示文本的最大像素宽度,如果传入字符串大于最大宽度,会收缩以适应最大宽度。但这个可选参数尚未得到大多数浏览器的支持

4.2 用fillText()绘制数字12

method param effect
fillText(string,x,y) string:要绘制的文本字符串</br>x:x坐标可选的最大像素宽度</br>y:y坐标可选的最大像素宽度 使用fillStyle属性绘制文本
strokeText(string,x,y) string:要绘制的文本字符串</br>x:x坐标可选的最大像素宽度</br>y:y坐标可选的最大像素宽度 使用strokeStyle属性为文本描边
var drawing = document.getElementById("drawing");  

// 确定浏览器支持<canvas>元素
if(drawing.getContext) { 
    var 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();  

    // 绘制数字12 
    context.font = "bold 14px Arial"; 
    context.textAlign = "center"; 
    context.textBaseline = "middle"; 
    context.fillText("12", 100, 20); 
}

</br>

4.3 给定宽度的区域,找到文本合适的字体大小

method param return effect
mearsureText(string) string类型:要确定大小的文本字符串 object:返回一个对象,该对象只有一个width属性 获得存有文本大小的对象
var fontSize = 100, 
      context.font = fontSize + "px Arial"; 
if (context.measureText) { 
    while(context.measureText("Hello world!").width > 140) { 
        fontSize--; 
        context.font = fontSize + "px Arial"; 
    } 
}  
context.fillText("Hello world!", 10, 10);
context.fillText("Font size is " + fontSize + "px", 10, 50);

上面的代码从100像素的字体大小开始递减,最终会找到合适的字体大小,在一个140像素的矩形区域中绘制文本 Hello world!

5. 变换

5.1 用translate()方法变换原点位置

var drawing = document.getElementById("drawing");

// 确定浏览器支持<canvas>元素 
if (drawing.getContext) { 
    var 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(); 
}


可以看出结果与3.2.1的图形相同,但把原点变换到时钟表盘中心点(100, 100)后,在同一方向上绘制线条的数学计算就变得简单起来了。

5.2 用rotate()方法旋转表针

var drawing = document.getElementById("drawing");

// 确定浏览器支持<canvas>元素 
if (drawing.getContext) { 
    var 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); 

    // 旋转表针,旋转弧度值为1,2π对应360°,则1对应(360/2π)°,约为57.30°
    context.rotate(1);

    // 绘制路径 
    context.stroke(); 
}

图中红色的部分不存在,是我做给大家的提示和比较而已,用来指明不加最后一句代码的指针原来的位置。


5.3 跟踪上下文的状态变化

栈:先进后出。

method param effect
save() null 当下所有设置都会进入一个栈结构
restore() null 在保存设置的栈结构中向前返回一级
var drawing = document.getElementById("drawing");

// 确定浏览器支持<canvas>元素
if (drawing.getContext) {

    var context = drawing.getContext("2d");

    context.fillStyle = "#ff0000"; 
    context.save();  

    context.fillStyle = "#00ff00"; 
    context.translate(100, 100); 
    context.save();  

    context.fillStyle = "#0000ff"; 
    //从点(100,100)开始绘制蓝色矩形  
    context.fillRect(0,0,100,200); 

    context.restore();  
    //从点(110,110)开始绘制绿色矩形  
    context.fillRect(10,10,100,200);

    context.restore(); 
    //从点(0, 0)开始绘制红色矩形
    context.fillRect(0,0,100,200); 
}

6. 绘制图像

把一幅图像绘制到画布上,可以使用drawImage()方法。

method param effect
drawImage(image,</br>tx,ty) image:要绘制的图像</br>tx:目标图像的x坐标</br>ty:目标图像的y坐标 把图像绘制到画布上,起点坐标为(tx,ty)
drawImage(image,</br>tx,ty,twidth,theight) image:要绘制的图像</br>tx:目标图像的x坐标</br>ty:目标图像的y坐标</br>twidth:目标图像的宽度</br>theight:目标图像的高度 把图像绘制到画布上,起点坐标为(tx,ty),绘制到画布上的图像的宽度为twidth,高度为theight
drawImage(image,</br>sx,sy,swidth,sheight,</br>tx,ty,twidth,theight) image:要绘制的图像</br>sx:源图像的x坐标</br>sy:源图像的y坐标</br>swidth:源图像的宽度</br>sheight:源图像的高度</br>tx:目标图像的x坐标</br>ty:目标图像的y坐标</br>twidth:目标图像的宽度</br>theight:目标图像的高度 先对源图像做一些处理,该处理为:以(sx,sy)为起点截取宽度为swidth,高度为sheight的新图像。处理后再把新图像绘制到画布上,起点坐标为(tx,ty),绘制到画布上的新图像的宽度为twidth,高度为theight

7. 阴影

attribute value effect
shadowColor String类型:颜色名、十六进制码、rgb等,如"#0000ff",默认为黑色 用CSS颜色格式表示的阴影颜色
shadowOffsetX Number类型,默认为0 形状或路径x轴方向的阴影偏移量
shadowOffsetY Number类型,默认为0 形状或路径y轴方向的阴影偏移量
shadowBlur Number类型,默认为0,即不模糊 模糊的像素数

8. 渐变

method param return
createLinearGradient 起点的x坐标</br>起点的y坐标</br>终点的x坐标</br>终点的y坐标 返回CanvasGradient对象的实例

9. 模式

10. 使用图像数据

11. 合成

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

推荐阅读更多精彩内容