最近笔者在学习HTML5的新元素<canvas>,会分享一些基础知识以及小例子,最终使用<canvas>实现一个绘制简单图表(条形图、线图或者饼图)的js库,会更新一到两篇文章~
下面我们开始吧~

确认宽度和高度

我们首先应该指定<canvas>标签即画布的宽度和高度属性,并在开始和闭合标签之间添加后备信息:

<html>
<head>
<meta charset="utf-8">
</head>
<body>
<canvas id="canvas" width="500" height="500">Canvas is not supported.</canvas>
</body>
</html>

取得绘图上下文

调用canvasgetContext()方法,这个方法接收一个参数,即上下文的名字:

var canvas = document.getElementById("canvas");
if(canvas.getContext) {
var context = canvas.getContext("2d")
}

上述例子中,我们在调用getContext()方法时,首先检测其是否存在,这是由于有的浏览器遇到HTML规范之外的标签时,也会创建一个DOM对象,比如我们在Chrome中尝试以下代码:

<ppp id="ppp"></ppp>
document.getElementById("ppp");
//<ppp id="ppp"></ppp>

因此即使当前浏览器不能兼容HTML规范中的canvas元素,同样会创建DOM对象,可其中却不存在getContext()
context上下文对象中包含了绘图需要的一系列属性和方法,大家在阅读本文时,记得区分属性和方法,改变属性会影响到后续的绘图效果,而调用方法往往是一次性的。

绘制简单的2D图形

坐标原点

2D上下文的坐标默认开始于左上角,原点坐标为(0, 0),使用translate(x, y)可以更改坐标原点。

填充

填充,就是用指定的样式,比如颜色、渐变或图像填充指定区域,对应的上下文属性为fillStyle,默认值为#000000, 比如:

context.fillStyle = "orange";
描边

描边就是在指定区域边缘画指定样式的线,比如:

context.strokeStyle = "grey";
绘制矩形

矩形是唯一一个可以在2D上下文中直接话的形状,其他的都需要绘制路径,可以使用3个方法直接绘制矩形。

fillRect(x, y, width, height)

可以绘制一个由fillStyle指定填充样式的矩形,比如:

context.translate(100, 100)
context.fillStyle = "#99cccc";
context.fillRect(-100, -50, 200, 100);
context.fillStyle="#3399cc";
context.fillRect(-60, -30, 120, 60);

strokeRect(x, y, width, height)

可以绘制一个由strokeStyle lineWidth lineCap lineJoin等属性指定描边样式的矩形,比如:

context.strokeStyle = "#99cccc";
context.lineWidth = "50";
context.lineJoin = "bevel";
context.strokeRect(0, 0, 400, 200);

属性名 含义 取值
lineCap 线条末端的形状 butt平头round圆头square方头
lineWidth 线条宽度 整数
lineJoin 线条相交的方式 round圆交bevel斜交mitter斜接
clearRect(x, y, width, height)

可以清楚画布上的指定区域,比如第一个例子中的两个矩形,我们将中间一小块清除:

context.translate(100, 100)
context.fillStyle = "#99cccc";
context.fillRect(-100, -50, 200, 100);
context.fillStyle="#3399cc";
context.clearRect(-60, -30, 120, 60);

绘制路径

使用路径我们可以绘制出比较复杂的图形,开始绘制前,首先执行:

context.beginPath();

结束绘制时,执行:

context.closePath();

以下我们列举了绘制路径的几个方法:

直线

lineTo(x, y),从当前游标至(x, y)画一条直线。

移动游标

moveTo(x, y) 将游标移动至(x, y),移动过程中不画线。比如:

context.beginPath();
context.moveTo(10, 10);
context.lineTo(50, 40);
context.moveTo(50, 50);
context.lineTo(100, 90);
context.stroke();
context.closePath();

弧线或圆形
arc(x, y, radius, startAngle, endAngle, counterclockwise)

从startAngle到endAngle绘制一条以(x, y)为圆心,radius为半径的弧线,其中startAngle和endAngle用弧度表示,couterclockwise为false时,顺时针画弧线, 反之,逆时针画弧线。

var canvas = document.getElementById("canvas");
if(canvas.getContext) {
var context = canvas.getContext("2d");
context.beginPath();
context.arc(400, 400, 50, arcUnit()*30, arcUnit()*180, false);
context.stroke();
}
function arcUnit() {
return Math.PI/180;
}

arcTo(startX, startY, endX, endY, radius)

arcTo()方法将利用当前坐标、起点(startX,startY)和终点(endX,endY)这三个点所形成的夹角,绘制一段与夹角的两边相切并且半径为radius的圆上的弧线。弧线的起点就是当前坐标所在边与圆的切点,弧线的终点就是终点(endX,endY)所在边与圆的切点,并且绘制的弧线是两个切点之间长度最短的那个圆弧。此外,如果当前端点不是(startX,startY),arcTo()方法还将添加一条当前端点到(startX,startY)的直线线段。
如果大家还记得高中数学的话,我们应该可以猜到,使用这个方法画弧线大致有三种情况:
** 只有一种半径可以使弧线两端正好在起点和终点
** 如果半径过大,画不出弧线
** 如果半径较小,必定会有一条当前端点到起点的直线
我们举三个例子:
首先定义一些通用的

var context = canvas.getContext("2d");
var currentPoint = {
x: 0,
y: 0
};
var startPoint = {
x: 50,
y: 50
};
var endPoint = {
x: 100,
y: 0
};

然后绘制参考线

context.moveTo(currentPoint.x, currentPoint.y);
context.lineTo(startPoint.x, startPoint.y);
context.lineTo(endPoint.x, endPoint.y);
context.strokeStyle = "red";
context.stroke();

画第一条弧线

context.moveTo(currentPoint.x, currentPoint.y);
context.arcTo(startPoint.x, startPoint.y, endPoint.x, endPoint.y, 80);
context.strokeStyle = "grey";
context.stroke();

context.arcTo(startPoint.x, startPoint.y, endPoint.x, endPoint.y, 120);

context.arcTo(startPoint.x, startPoint.y, endPoint.x, endPoint.y, 40);

曲线
二次贝塞尔曲线

quadraticCurveTo(cpX, cpY, x, y)
上述方法可以画一条从当前位置到(x, y), 以(cpX, cpY)为控制点的贝塞尔曲线

三次贝塞尔曲线

bezierCurveTo(cpX1, cpY1, cpX2, cpY2, x, y)
上述方法可以画一条从当前位置到(x, y), 以(cpX1, cpY1)和(cpX2, cpY2)为控制点的贝塞尔曲线。

二次贝塞尔与三次贝塞尔的绘制涉及到比较复杂的数学运算,笔者在此就忽略啦...
当然,大部分前端人士可能都跟笔者一样,只希望能画出一条优美的曲线,并不关心实现细节,那么一般认为什么样的曲线才算是优美的曲线呢:

  1. 对称的曲线
  2. 有一个适度的弧度

根据以上规则,我们写一个工具方法:

function drawCurvePath( ctx, start, end, curveness ) {
var cp = [
( start.x + end.x ) / 2 - ( start.y - end.y ) * curveness,
( start.y + end.y ) / 2 - ( end.x - start.x ) * curveness
];
ctx.moveTo( start.x, start.y );
ctx.quadraticCurveTo(
cp[ 0 ], cp[ 1 ],
end.x, end.y
);
ctx.stroke();
}

以上参考自用canvas绘制一个曲线动画——深入理解贝塞尔曲线,大家可以前往了解更深入的贝塞尔曲线画法。

矩形

使用rect(x, y, width, height)可以绘制一个左上角坐标为(x, y),宽width,高height的矩形路径。

context.rect(300, 300, 100, 200);
context.stroke();

绘制文本

在绘制本文之前,如果有必要,我们首先应该指定context的几个属性, 比如:

context.font = "bold 14px Arial"; // 格式同css中指定字体样式
context.textAlign = "center"; // start end center left right
context.textBaseline = "middle"; // top hanging middle alphabetic ideographic bottom

fillText(text, x, y, maxWidth)使用fillStyle属性显示文本,strokeText(text, x, y, maxWidth)使用strokeStyle属性为文本描边。
使用measureText(text)方法可以获得文本的宽度。如果我们并不清楚指定的宽度够不够显示当前字体设置下的一段文字,可以使用如下方法:

var fontSize = 50;
var maxWidth = 100;
context.font = "bold " + fontSize+"px Arial";
var text = "Hello World!";
while(context.measureText(text).width > maxWidth) {
fontSize--;
context.font = "bold " + fontSize+"px Arial";
}
context.fillText(text, 50, 50, maxWidth);

变换

旋转

rotate(angle)

context.rotate(Math.PI/4)
context.fillText(text, 50, 50, maxWidth);

缩放

scale(scaleX, scaleY)

context.scale(1.2, 1.2);
context.fillText(text, 50, 50, maxWidth);
context.scale(0.5, 0.5);
context.fillText(text, 50, 50, maxWidth);

移动坐标原点

translate(x, y)
假如我们要绘制一个对称图形,移动坐标原点将会大大简化对坐标的计算。

矩阵变换

使用transform(scaleX,skewX,skewY,scaleY,transX,transY)可以进行矩阵变换,其实以上讲的三个方法本质上都在调用矩阵变换,从参数名中可以看出,它们分别表示X轴方向的缩放,Y轴方向的缩放,X轴方向的斜切,Y轴方向的斜切,X轴方向的偏移量,Y轴方向的偏移量。默认值分别为1 0 0 1 0 0。
我们现在使用transform()重新定义一遍之前的三个方法:
rotate

function rotate (ctx, degree) {
var unit = Math.PI/180;
ctx.transform(Math.cos(degree*unit),Math.sin(degree*unit),-Math.sin(degree*unit),Math.cos(degree*unit),0,0)
}

scale

function scale (ctx, scale) {
ctx.transform(scale.x, 0, 0, scale.y, 0, 0);
}

translate

function translate (ctx, translate) {
ctx.transform(1, 0, 0, 1, translate.x, translate.y);
}

我们现在绘制一段文本,先平移(50, 50),然后缩放2倍,最后旋转30度。

context.fillText(text, 50, 50, maxWidth);
translate(context, {x: 50, y: 50});
scale(context, {x: 2, y: 2});
rotate(context, 30);
context.fillText(text, 50, 50, maxWidth);


每次执行transform()都是基于上一次的结果,并不是初始状态,很多时候我们想执行的是基于初始状态旋转、平移或缩放,而非上一次的状态,此时我们可以使用setTransfrom(scaleX,skewX,skewY,scaleY,transX,transY),此方法首先会重置变换矩阵,然后执行transform()
更加详细的内容可以参考html5 Canvas画图教程26:用transform来实现位移,缩放,旋转等

绘制图像

使用drawImage(image, x1, y1, width1, height1, x2, y2, width2, height2)可以将图像的指定部分按照指定的大小绘制到画布指定的位置上。

参数 含义
image 要绘制的图像,可以是HTMLImageElement,也可以是canvas
x1 源图像的x坐标
y1 源图像的y坐标
width1 源图像的宽度
height1 源图像的高度
x2 画布的x坐标
y2 画布的y坐标
width2 图像在画布上显示的宽度
height2 图像在画布上显示的高度

绘制阴影

如果要给形状或路径加上阴影,我们要在绘制前设置context对象的以下属性:

属性 含义
shadowColor 阴影颜色
shadowOffsetX x轴偏移量
shadowOffsetY y轴偏移量
shadowBlur 模糊的像素数
context.shadowColor = "grey";
context.shadowOffsetX = "20";
context.shadowBlur = "5";
context.fillText(text, 50, 50, maxWidth);

渐变

线性渐变

使用createLinearGradient(startX, startY, endX, endY)来创建线性渐变,这个方法确认了渐变的起始和方向,然后我们通过addColorStop(position, color)来添加渐变的颜色,position是0到1的数字。一下笔者画一个超级喜欢的条纹渐变:

var grad = context.createLinearGradient(50, 50, 200, 200)
grad.addColorStop(0, "grey");
grad.addColorStop(0.3, "grey");
grad.addColorStop(0.3, "red");
grad.addColorStop(0.5, "red");
grad.addColorStop(0.5, "orange");
grad.addColorStop(0.7, "orange");
context.fillStyle = grad;
context.fillRect(50, 50, 150, 150);

径向渐变

使用createRadialGradient(centerX1, centerY1, radius1, centerX2, centerY2, radius2)创建径向渐变。

图像重复

使用createPattern(image, repeatType)可以绘制重复的图像,用来填充或描边,第一个参数是要重复的HTMLImageElement或canvas或video,第二个参数表示如何重复该图像,可取repeat repeat-x repeat-y no-repeat

HTML5 Canvas(基础知识)的更多相关文章

  1. HTML5 Canvas基础知识

    HTML5画布 1.创建一个画布         <canvas id="myCanvas" width="200" height="100&q ...

  2. 《HTML5 CANVAS基础教程》读书笔记

    一.HTML5简介 1.HTML5新特性 1)结构元素:section,header,hgroup,footer,nav,article,aside, 2)内容元素:figure,figcaption ...

  3. canvas 基础知识整理(二)

    html部分: <canvas id="myCanvas" width="800" height="800" ></can ...

  4. canvas 基础知识整理(一)

    canvas这个 HTML 元素是为了客户端矢量图形而设计的.它自己没有行为,但却把一个绘图 API 展现给客户端 JavaScript 以使脚本能够把想绘制的东西都绘制到一块画布上. html的基本 ...

  5. canvas基础知识

    canvas基础知识 ## CanvasDOM对象 #### 获取绘图环境```canvas.getContext();``` #### 设置宽和高```canvas.width = 500;canv ...

  6. canvas API ,通俗的canvas基础知识(四)

    今天要讲的内容是canvas的转换功能,前面的内容没用看的同学可以出门右转,先看看前面的基础知识,废话不多说,开始进入正题吧! 何为转换功能?熟悉css3的同学都知道,css3里面有transform ...

  7. canvas 基础知识

    canvas 基础 低版本的ie不支持html5,需要引入excanvas.js来让ie支持canvas. 检测支持canvas <canvas id="canvas" wi ...

  8. canvas API ,通俗的canvas基础知识(一)

    在没学canvas的时候,觉得canvas是这么的神秘,这么的绚丽,这么的高深,用canvas做出来的效果是如此的炫酷,能做的事情如此的宽广,简直让我心生敬畏之心,时常感叹:我要是得此技能,必定要上天 ...

  9. HTML5 <canvas> 基础学习

    HTML5 <canvas> 元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成. <canvas> 标签只是图形容器,您必须使用脚本来绘制图形 创建一个画布( ...

  10. HTML5 canvas准备知识

    利用canvas来进行画图工作.因此,我们有必要进行一些画图方面的术语说明. 一.画布 在日常生活中,如果我们要画画,可以找纸.板.画布等等工具.而在网页元素中,我们只需要定义一个标签即可. < ...

随机推荐

  1. 对xgboost中dump_model生成的booster进行解析

    xgboost原生包中有一个dump_model方法,这个方法能帮助我们看到基分类器的决策树如何选择特征进行分裂节点的,使用的基分类器有两个特点: 二叉树: 特征可以重复选择,来切分当前节点所含的数据 ...

  2. bzoj1076 奖励关(概率dp)(状态压缩)

    BZOJ 1076 [SCOI2008]奖励关 Description 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须 ...

  3. 利用离散 Fourier 变换解一元二次方程

    设二次方程$$x^2+bx+c=0$$的两个根分别为 $x_1,x_2$.则$$(x-x_1)(x-x_2)=x^2+bx+c.$$因此$$\begin{cases}  x_1+x_2=-b\\x_1 ...

  4. 59)PHP,管理员表中所存在的项

    用户ID 用户名 用户密码 用户权限(就是他的角色等级,比如是1级  2级,  三级等等) 上次登录的IP 上次登录的时间

  5. OSI体系结构(七层)

    OSI体系结构,意为开放式系统互联.国际标准组织(国际标准化组织)制定了OSI模型.这个模型把网络通信的工作分为7层,分别是物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. 1至4层被认为 ...

  6. python 3新式类的多继承

    因为我用的是python3,所以所用到的类都是新式类,这里我说的都是新式类,python2类的继承复杂一些,主要有新式类和老式类.python3类(新式类)的继承是是广度优先(BFS),实例如下: c ...

  7. Lua 学习 chapter30 编写c函数的技巧 - Jow的博客

    目录 数组操作 字符串操作 在c函数中保存状态 生活总需要一点仪式感,然后慢慢的像那个趋向完美的自己靠近. 数组操作 Lua中的数组就是以特殊的方式使用边.像lua_setttable and lua ...

  8. JS去重函数的扩展应用

    数据: 账单id[1,1,1,1,2,2,2,3,3,3,3,3,3,3], 相对于账单id的金额[100,120,110,150,200,180,220,115,150,120,180,220,14 ...

  9. [洛谷P3366] [模板] 最小生成树

    存个模板,顺便复习一下kruskal和prim. 题目传送门 kruskal 稀疏图上表现更优. 设点数为n,边数为m. 复杂度:O(mlogm). 先对所有边按照边权排序,初始化并查集的信息. 然后 ...

  10. JS做深度学习1——偶然发现与入门

    JS做深度学习1--偶然发现与入门 不久前,我初次涉猎了Node.js,并且使用它开发了毕业设计的WEB模块,然后通过在Node中调用系统命令执行Python文件方式实现了深度学习功能模块的对接,Py ...