掌握Canvas,从这里开始

Canvas是HTML5中一个重要的组件,提供了一系列接口使得可以开发者可以利用Js绘制各种图表、动画。

Canvas浏览器检测

如果你的代码需要兼容旧的浏览器,则可以如下代码来检测:

const canvas = document.querySelector("#canvas");
if (canvas.getContext) {
    // do something
} else {
    // fallback
}

或者使用Modernizr库来识别浏览器是否支持Canvas:

if (Modernizr.canvas) {
    // canvas code
} else {
    // fallback code
}

不过对于大家常用的现代浏览器来说,Canvas已经算是标配了。所以上述代码加不加其实主要看产品的需求。

Canvas 特点

Canvas提供了一系列绘图接口,且都是基于像素的,所以我们可以利用Canvas实现很多有趣的功能,比如截图、图片渲染等。常用的接口主要有画图片,文本,线条以及gradients等,Canvas还支持和CSS3 的transform类似的变换操作,不过不支持动画(animation)。

我们在使用canvas时,主要有以下几个步骤:

  • 定义一个canvas元素
  • 设置canvas的一些属性(如宽高等)
  • 获得2d context上下文对象
  • 画图

例子如下:

const canvas = document.createElement("canvas");
canvas.width = 200;
canvas.height = 200;
const context = canvas.getContext("2d");
context.fillRect(0, 0, 20, 20);

document.body.appendChild(canvas);

基础知识

在Canvas中,画布的坐标空间是从左上角开始的:

[图片上传失败...(image-44c488-1578909996036)]

所有绘制的元素都会基于原点进行定位,不过canvas中也提供了接口来变换原点位置。

矩形和椭圆

在canvas中如果你想要画矩形的话,主要有以下3个方法:

  • clearRect(x, y, width, height): 清除指定矩形区域
  • fillRect(x, y, width, height): 绘制填充的矩形
  • strokeRect(x, y, w, h): 绘制一个矩形边框

对于画布上下文的设置,主要利用fillStylestrokeStyle两个属性设置颜色等样式。

const canvas = document.querySelector("#mycanvas");

const ctx = canvas.getContext("2d");

ctx.fillRect(20, 20, 100, 200); // 绘制一个宽100,高100的矩形

而要画一个椭圆的话,主要借助两个接口:

  • arc(x, y, radius, startAngle, endAngle, antiClockwise): 画一个以(x, y)为圆心,radius为半径的圆弧,从startAngle开始到endAngle结束,antiClosewise为绘制方向,默认是顺时针
  • arcTo(x1, y1, x2, y2, radius): 根据(x1, y1)和(x2, y2),已经半径来绘制圆弧,再以直线连接两个控制点
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

ctx.arc(50, 50, 10, 0, Math.PI * 0.5, false);
ctx.fill();

线段和路径

  1. 首先,你需要创建路径起始点。
  2. 然后你使用画图命令去画出路径。
  3. 之后你把路径封闭。
  4. 一旦路径生成,你就能通过描边或填充路径区域来渲染图形。
  • beginPath(): 新建路径,生成之后,图形绘制命令被指向到路径上生成路径
  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y): 三次贝塞尔曲线,(cp1x,cp1y)为控制点一,(cp2x,cp2y)为控制点二,(x,y)为结束点
  • closePath(): 闭合路径
  • moveTo(x, y): 移动到某个点,以这个点为基础来绘制图形
  • quadraticCurveTo(cpx, cpy, x, y): 二次贝塞尔曲线,(cpx,cpy)为一个控制点,(x,y)为结束点
  • stroke(): 绘制轮廓
  • fill(): 填充路径内容区域

主要使用的属性有:

lineCap, lineJoin, lineWidth, miterLimit

以下是例子:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

// 绘制一个三角形
ctx.beginPath(); // 生成路径
ctx.moveTo(75, 50); // 以 (75, 50)这个坐标为起点开始绘制
ctx.lineTo(100, 75); // 移动到(100,75)这个坐标画一条线
ctx.lineTo(100, 25); // 再移动到 (100, 25)这个坐标画一条线
ctx.fill(); // 填充图形,没有闭合的图形会自动闭合,所以无需调用closePath接口

文本

绘制文本我们主要用以下两种接口:

  • fillText(text, x, y, [maxWidth]): 在x, y处绘制指定文本,可以选择最大宽度
  • strokeText(text, x, y, [maxWidth]): 在x, y处绘制指定文本的边框,可以选择最大宽度
ctx.font = "16px serif";
ctx.strokeText("Hello", 50, 50);

图片

图片绘制也是canvas中常用的功能,可以使用如下接口来实现:

  • drawImage(image, x, y, [width], [height]): 其中 image 是 image 或者 canvas 对象,(x,y)为绘制的起点
const image = new Image();
image.onload(function() {
    ctx.drawImage(image, 0, 0, 100, 100);
});
image.src = "http://xxxx";

动画

可以利用canvas绘制动画,一个动画由很多帧组成,每一帧都需要以下这些步骤:

  1. 清空canvas
  2. 保存canvas
  3. 绘制图形
  4. 恢复canvas

然后可以使用requestAnimationFrame来执行一个动画,对于旧的浏览器则使用setInterval来调用回调函数。

例子可以参考: https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Basic_animations

Path2D

Path2D 是一个比较新的对象,利用它可以缓存和记录已有的路径信息,以便在后续复用。

主要有以下三种方式新建一个Path2D对象:

  • new Path(): 空的path对象
  • new Path2D(path): 克隆一个新的Path对象
  • new Path2D(d): 从SVG建立Path对象, d 为svg路径字符串如: "M10 10 h 80 v 80 h -80 Z"
const 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);

接口

最后来列举一下Canvasa中所有常见的接口以及对应的功能,在忘记的时候能够快速索引到:

接口 功能描述
addColorStop 绘制过渡效果是添加结束点的颜色
drawImage 绘制图片
restore 从栈中恢复上下文
arc 绘制弧线
fill 填充
rotate(angle) 以原点为中心旋转canvas
arcTo 绘制圆弧
fillRect 填充矩形
save 将上下文保存到栈中
beginPath 开始路径
fillText 填充文本
scale(x, y) 缩放画布
bezierCurveTo 曲线
getImageData 获得图片数据
setTransform(m11, m12, m21, m22, dx, dy) 设置变换矩阵
clearRect 清楚矩形区域
isPointInPath 判断像素点是否在对应的路径上
stroke 绘制轮廓
clip 裁剪
lineTo 绘制直线
strokeRect 绘制矩形轮廓
closePath 关闭路径
measureText 返回一个包含指定文本宽度的对象(以像素为单位)
strokeText 绘制文本轮廓
createImageData 创建图片像素数据
moveTo 移动到某点,并以这个点为基础绘制图形
transform(m11, m12, m21, m22, dx, dy) 使用变换矩阵
createLinearGradient 创建一个线性过渡对象
putImageData 将图像数据放回画布
translate 移动canvas和原点到(x,y)上
createPattern 图像填充,在指定的方向内重复指定的元素
quadraticCurveTo 二次贝塞尔曲线
createRadialGradient 创建一条放射颜色渐变
rect 创建矩形

API 属性有:

属性 描述
data Uint8ClampedArray类型的一维数组,包含着RGBA格式的整型数据,范围在0至255之间(包括255),ImageData对象的属性
miterLimit miterLimit 属性就是用来设定外延交点与连接点的最大距离,如果交点距离大于此值,连接效果会变成了 bevel。
fillStyle 填充颜色
shadowBlur 阴影的模糊程度,默认为0
font 绘制文本时的字体,默认的字体是 10px sans-serif
shadowColor 阴影颜色,默认是全透明的黑色
globalAlpha 透明度,从0.0 到 1.0
globalCompositeOperation 在画新图形时采用的遮盖策略,其值是一个标识12种遮盖方式的字符串,参考
lineCap 线条末端样式,butt, roundsquare, 默认值是butt`
lineJoin 线条与线条间接合处的样式,有3个值:round, bevelmiter
lineWidth 线条宽度
shadowOffsetX 用来设定阴影在X轴的延伸距离
shadowOffsetY 用来设定阴影在Y 轴的延伸距离
strokeStyle 图形轮廓颜色
textAlign 绘制文本的对齐选项,可选的值包括:start, end, left, right or center. 默认值是 start
textBaseline 绘制文本时的基线对齐选项. 可选的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默认值是 alphabetic。
direction 绘制文本的方向,可能的值包括:ltr, rtl, inherit。默认值是 inherit

参考资料

https://html.spec.whatwg.org/multipage/canvas.html

https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes

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

推荐阅读更多精彩内容