canvas 基本知识


什么是 canvas


canvas 是 HTML5 新定义的标签,通过使用脚本(通常是 JavaScript)绘制图形。
<canvas> 标签只是图形容器,相当于一个画布,canvas元素本身是没有绘图能力的。所有的绘制工作必须在 JavaScript 内部完成,相当于使用画笔在画布上画画。

默认情况下,<canvas> 没有边框和内容。默认是一个 300*150 的画布,所以我们创建了 <canvas> 之后要对其设置宽高。

我们可以通过html属性‘width’,‘height’来设置canvas的宽高,不可以通过 css 属性来设置宽高。因为通过 css 属性设置的宽高会使 canvas 内的图像按照 300150 时的比例放大或缩小*

getContext()


context 是一个封装了很多绘图功能的对象,我们在页面中创建一个 canvas 标签之后,首先要使用 getContext() 获取 canvas 的上下文环境,目前 getContext() 的参数只有 2d,暂时还不支持3d

getContext("2d") 对象是内建的 HTML5 对象,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法

canvas 元素绘制图像


canvas 创建图形有两种方式

context.fill()

fill() 方法填充当前的图像(路径)。默认颜色是黑色。在填充前要先使用 fillStyle 设置填充的颜色或者渐变,并且如果路径未关闭,那么 fill() 方法会从路径结束点到开始点之间添加一条线,以关闭该路径(正如
closePath() 一样),然后填充该路径

context.stroke()

stroke() 方法会实际地绘制出通过 moveTo()lineTo() 方法定义的路径。默认颜色是黑色。在进行图形绘制前,要设置好绘图的样式

 fillStyle()//填充的样式
strokeStyle()//边框样式
context.lineWidth()//图形边框宽度

绘制矩形

用 canvas 绘制一个矩形很简单

fillRect(x,y,width,height) // 实心矩形 
strokeRect(x,y,width,height) // 空心矩形
  • x :起始点的 x 坐标
  • y :起始点的 y 坐标
  • width : 矩形的宽
  • height : 矩形的高
//html代码
<canvas id="canvas"></canvas>
//script代码
 var canvas = document.getElementById('canvas');
 var context = canvas.getContext('2d');
 context.fillRect(0, 0, 100, 100);
 context.strokeRect(120, 0, 100, 100);

显示如下:

image

canvas绘制矩形有填充颜色
我们可以看出,在没有设置颜色的情况下,默认是黑色的。

我们还可以通过设置 fillStyle 改变其填充颜色

context.fillStyle = "pink";
context.strokeStyle = "darkred";
context.fillRect(0, 0, 100, 100);
context.strokeRect(120, 0, 100, 100);

效果如下:


image

canvas绘制矩形有填充颜色

清除矩形区域

clearRect(x,y,width,height)
  • x :清除矩形起始点的 x 坐标
  • y :清除矩形起始点的 y 坐标
  • width : 清除矩形矩形的宽
  • height : 清除矩形矩形的高
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
context.fillRect(0, 0, 100, 100);
context.strokeRect(120, 0, 100, 100);
context.fillStyle = "pink";
context.strokeStyle = "darkred";
context.fillRect(0, 120, 100, 100);
context.strokeRect(120, 120, 100, 100);
context.clearRect( 50,50,120,120)

效果如下:


image

清除矩形

实心圆

context.arc(x, y, radius, starAngle,endAngle, anticlockwise)

  • x : 圆心的 x 坐标
  • y:圆心的 y 坐标
  • radius : 半径
  • starAngle :开始角度
  • endAngle:结束角度
  • anticlockwise :是否逆时针(true)为逆时针,(false)为顺时针
context.beginPath();
context.arc(300, 350, 100, 0, Math.PI * 2, true);
//不关闭路径路径会一直保留下去
context.closePath();
context.fillStyle = 'rgba(0,255,0,0.25)';
context.fill();

效果如下:


image

圆弧

如果不填充颜色,实心圆就是圆弧

 context.beginPath();
 context.arc(600, 350, 100, 0, Math.PI , true);
 context.strokeStyle = 'pink';
 context.closePath();
 context.stroke();

 context.beginPath();
 context.arc(300, 350, 100, 0, Math.PI , true);
 context.strokeStyle = 'red';
 //没有closePath
 context.stroke();

效果如图:


image

canvas绘制圆弧

  • 系统默认在绘制第一个路径的开始点为beginPath
  • 如果画完前面的路径没有重新指定beginPath,那么画第其他路径的时候 会将前面最近指定的beginPath后的全部路径重新绘制
  • 每次调用context.fill()的时候会自动把当次绘制的路径的开始点和结束+ 点相连,接着填充封闭的部分

所以说,如果第一个圆弧没有 closePath() 并且第二个圆弧没有 beginPath() 的话就是这样的效果:

image

绘制线段

  • moveTo(x,y):把路径移动到画布中的指定点,不创建线条
  • lineTo(x,y):添加一个新点,然后在画布中创建从该点到最后指定点的线条
  • 每次画线都从 moveTo 的点到lineTo的点,
    效果如下:
    image

    如果没有 moveTo 那么第一次 lineTo 的效果和 moveTo 一样
    例如:
 context.strokeStyle = 'pink';
 context.lineTo(100, 100);
 context.lineTo(200, 200);
 context.stroke();*/

效果如下:

image

每次lineTo后如果没有moveTo,那么下次lineTo的开始点为前一次lineTo的结束点
例如:

// 绘制片段
 context.strokeStyle = 'pink';
 context.lineTo(200, 200);
 context.lineTo(200, 100);
 context.lineTo(100,50);
 context.stroke();

效果如下:


image

我们可以使用 canvas 的线段绘制各种各样的图形,比如绘制一个六边形

var n = 0;
 var dx = 150;
 var dy = 150;
 var s = 100;
 context.beginPath();
 context.fillStyle = 'pink';
 context.strokeStyle = 'rgb(0,0,100)';
 var x = Math.sin(0); // x 的正玄值。返回值在 -1.0 到 1.0 之间;
 var y = Math.cos(0); // x 的余弦值。返回的是 -1.0 到 1.0 之间的数;
 var dig = Math.PI / 15 * 5;
 for (var i = 0; i < 6; i++) {
 var x = Math.sin(i * dig);
 var y = Math.cos(i * dig);
 context.lineTo(dx + x * s, dy + y * s);
 console.log( x ,y )
 }
 context.closePath();
 context.fill();
 context.stroke();
image

绘制 30 边形:

var n = 0;
 var dx = 150;
 var dy = 150;
 var s = 100;
 context.beginPath();
 context.fillStyle = 'pink';
 context.strokeStyle = 'rgb(0,0,100)';
 var x = Math.sin(0);
 var y = Math.cos(0);
 var dig = Math.PI / 15 * 7;
 for (var i = 0; i < 30; i++) {
 var x = Math.sin(i * dig);
 var y = Math.cos(i * dig);
 context.lineTo(dx + x * s, dy + y * s);
 console.log( x ,y )
 }
 context.closePath();
 context.fill();
 context.stroke();

效果如下:


image

线性渐变

var lg= context.createLinearGradient(xStart,yStart,xEnd,yEnd)
lg.addColorStop(offset,color)

  • xstart:渐变开始点x坐标
  • ystart:渐变开始点y坐标
  • xEnd:渐变结束点x坐标
  • yEnd:渐变结束点y坐标
  • offset:设定的颜色离渐变结束点的偏移量(0~1)
  • color:绘制时要使用的颜色
    例如:
 var g1 = context.createLinearGradient(0, 0, 0, 300);
 g1.addColorStop(0, '#E55D87'); 
 g1.addColorStop(1, '#5FC3E4');
 context.fillStyle = g1;
 context.fillRect(0, 0, 400, 300);

效果如下:


image

径向渐变

var rg=context.createRadialGradient(xStart,yStart,radiusStart,xEnd,yEnd,radiusEnd)
rg.addColorStop(offset,color)

  • xStart:发散开始圆心x坐标
  • yStart:发散开始圆心y坐标
  • radiusStart:发散开始圆的半径
  • xEnd:发散结束圆心的x坐标
  • yEnd:发散结束圆心的y坐标
  • radiusEnd:发散结束圆的半径
  • offset:设定的颜色离渐变结束点的偏移量(0~1)
  • color:绘制时要使用的颜色


    image

    径向渐变原理

例如:

// 同心圆径向渐变
 var g1 = context.createRadialGradient(200, 150, 0, 200, 150, 200);
 g1.addColorStop(0.1, '#F09819');
 g1.addColorStop(1, '#EDDE5D');
 context.fillStyle = g1;
 context.beginPath();
 context.arc(200, 150, 100, 0, Math.PI * 2, true);
 context.closePath();
 context.fill();
image

canvas绘制同心圆径向渐变

//不同圆心的径向渐变模型
 var g1 = context.createRadialGradient(100, 150, 10, 300, 150, 80);
 g1.addColorStop(0.1, '#F09819');
 g1.addColorStop(0.8, 'red');
 g1.addColorStop(1, '#EDDE5D');

 context.fillStyle = g1;
 context.fillRect(0, 0, 300, 500);

效果图:


image

不同圆心径向渐变

图形变形

缩放

scale(x,y)

  • x :x坐标轴按 x 比例缩放
  • y :x坐标轴按 y 比例缩放
    旋转
    rotate(angle)
  • angle :坐标轴旋转x角度(角度变化模型和画圆的模型一样)
平移

translate(x,y)

  • x :坐标原点向x轴方向平移x
  • y :坐标原点向y轴方向平移y
    平移,缩放,旋转先后顺序不同,坐标轴的变化图,图片来源于网络:


    image

    平移缩放旋转先后顺序不同坐标轴的变化图

图形组合

globalCompositeOperation=type
设置或返回新图像如何绘制到已有的图像上。最后的效果取决于 type 的值
type:

  • source-over(默认值):在原有图形上绘制新图形
  • destination-over:在原有图形下绘制新图形
  • source-in:显示原有图形和新图形的交集,新图形在上,所以颜色为新图形的颜色
  • destination-in:显示原有图形和新图形的交集,原有图形在上,所以颜色为原有图形的颜色
  • source-out:只显示新图形非交集部分
  • destination-out:只显示原有图形非交集部分
  • source-atop:显示原有图形和交集部分,新图形在上,所以交集部分的颜色为新图形的颜色
  • destination-atop:显示新图形和交集部分,新图形在下,所以交集部分的颜色为原有图形的颜色
  • lighter:原有图形和新图形都显示,交集部分做颜色叠加
  • xor:重叠飞部分不现实
  • copy:只显示新图形
    效果图如下,图片来源于网络


    image

阴影

shadowOffsetX:设置或返回阴影距形状的水平距离(默认值为 0)
shadowOffsetY:设置或返回阴影距形状的垂直距离(默认值为 0)
shadowColor:设置或返回用于阴影的颜色
shadowBlur:设置或返回用于阴影的模糊级别(值越大越模糊)

例如:

 context.fillStyle = 'white';
 context.beginPath();
 context.arc(100,100,10,0,2 * Math.PI);
 context.shadowColor = 'white';
 context.shadowBlur = 10;
 context.fill();
 context.closePath();

我们看到的效果就是我们在开头提起的例子中的 star 粒子的效果,因为其有白色阴影的效果,所以看起来像是发光一样,效果如下图:


image

图像绘制

drawImage()
向画布上绘制图像、画布或视频

  • 在画布上定位图像:context.drawImage(img,x,y);
  • 在画布上定位图像,并规定图像的宽度和高度: context.drawImage(img,x,y,width,height);
  • 剪切图像,并在画布上定位被剪切的部分:context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
  • img:规定要使用的图像、画布或视频。
  • sx:可选。开始剪切的 x 坐标位置。
  • sy:可选。开始剪切的 y 坐标位置。
  • swidth:可选。被剪切图像的宽度。
  • sheight:可选。被剪切图像的高度。
  • x:在画布上放置图像的 x 坐标位置。
  • y:在画布上放置图像的 y 坐标位置。
  • width:可选。要使用的图像的宽度。(伸展或缩小图像)
  • height:可选。要使用的图像的高度。(伸展或缩小图像)
    image

图像平铺

createPattern(image,type)
type:

  • no-repeat:不平铺
  • repeat-x:横方向平铺
  • repeat-y:纵方向平铺
  • repeat:全方向平铺

图像裁剪

clip()从原始画布剪切任意形状和尺寸的区域,需要先创建裁剪区域,再绘制图像;一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。您也可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)。
例如:

 // 设置剪切区域(粉色矩形)
 context.rect(0,0,500,400);
 context.fillStyle = "pink";
 context.fill();
 context.clip();

 // 在剪切区域中绘制图形(白色矩形)
 context.fillStyle = "white";
 context.fillRect(10,10,100,100);

 // 之后绘制的图形只能显示在剪切区域之内(红色矩形)
 context.fillStyle = "red";
 context.fillRect(100,100,600,600)

效果如下:可以看到我们设置的红色矩形是一个 600600 的矩形,但是显然是没有显示完的,*一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。


image

所以说我们可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)。
代码如下:

context.save();
 // 设置剪切区域
 context.rect(0,0,500,400);
 context.fillStyle = "pink";
 context.fill();
 context.clip();

 // 在剪切区域中绘制图形
 context.fillStyle = "white";
 context.fillRect(10,10,100,100);

 context.restore();
 // 之后绘制的图形只能显示在剪切区域之内
 context.fillStyle = "red";
 context.fillRect(100,100,600,600)

这样就可以正常显示了:


image

绘制文字

fillText(text,x,y):绘制实心文字
strokeText():绘制文字描边(空心)
textAlign:设置或返回文本内容的当前对齐方式
textBaseline:设置或返回在绘制文本时使用的当前文本基线
font:设置或返回文本内容的当前字体属性

例如:

 context.font="40px Arial";
 context.fillText("Hello world",200,200);
 context.strokeText("Hello world",200,300)

效果如下:


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

推荐阅读更多精彩内容

  • 一:canvas简介 1.1什么是canvas? ①:canvas是HTML5提供的一种新标签 ②:HTML5 ...
    GreenHand1阅读 4,674评论 2 32
  •   HTML5 添加的最受欢迎的功能就是 元素。这个元素负责在页面中设定一个区域,然后就可以通过 JavaScri...
    霜天晓阅读 3,003评论 0 2
  • canvas元素的基础知识 在页面上放置一个canvas元素,就相当于在页面上放置了一块画布,可以在其中进行图形的...
    oWSQo阅读 10,278评论 0 19
  • 【Android 自定义View之绘图】 基础图形的绘制 一、Paint与Canvas 绘图需要两个工具,笔和纸。...
    Rtia阅读 11,653评论 5 34
  • 本文首发于我的个人博客:http://cherryblog.site/github项目地址:https://git...
    sunshine小小倩阅读 1,977评论 1 8