在canvas中画图其实也和现实中的画图是一样的,只不过是我们要告诉机器来怎么画。比如我们使用2b铅笔画了一条三厘米的直线,那么转换成canvas能识别的语言就是,我们拿了个什么颜色的笔strokeStyle
,笔的线宽lineWidth
,以及开始点和结束点,按照这样一系列的指令,canvas就可以画出我们想要的图形了。我们来看下具体的基本图形的使用。
线段
canvas中,线段需要用路径来实现,然后进行描边。设置路径用beginPath()
开始路径,closePath()
结束路径。我们可以设置笔触的颜色strokeStyle
和线段的宽度lineWidth
。线段有头尾有拐点,头尾端点的样式可以用lineCap
设置,拐点的样式可以用lineJoin
设置。
lineCap属性定义线的端点,有三个值:
- butt: 默认值,端点是垂直于线段边缘的平直边缘
- round:端点是线段边缘处以线宽为直径的半圆
- square:端点是线段边缘处以线宽为长、以一半线宽为宽的矩形
lineJoin属性定义两条线相交产生的拐角。也有三个值:
- miter:默认值,在连接处边缘延长相接。miterLimiter是角长和线宽所允许的最大比例(默认是10)
- bevel:连接处是一个对角线斜角
- round:连接处是一个圆实例:
1 | function drawLine(context) { |
弧线
arc(x, y, radius, startAngle, endAngle, anticlockwise);
x和y定义圆心位置,radius定义弧线半径,startAngle和endAngle定义起始和结束的弧度值,anticlockwise表示是否是逆时针,true或false。
1 | var sangle = 0, angle = 90; |
贝塞尔曲线
贝塞尔曲线有平方和立方两种形式。
平方贝塞尔曲线
在二维空间中,贝塞尔曲线通过“起点”、“终点”以及两个决定曲线走向的“控制点”定义完成。平方贝塞尔曲线是最简单的曲线,只需要一个终点以及一个控制点。
1 | quadraticCurveTo(cpx, cpy, x, y) |
其中控制点为(cpx,cpy),终点为(x,y)。好比是起点到终点的直线,通过控制点将直线拉成弧线,越靠近控制点的地方力度越大。
1 | // 贝塞尔平方曲线 |
立方贝塞尔曲线
立方贝塞尔曲线用两个控制点,从而很容易绘制S型曲线。
1 | bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) |
1 | var p1 = new Point(50, 150); |
矩形
- fillRect(x, y, w, h):在位置(x, y)处以宽为w,高为y绘制一个填充矩形。(需要先设置好填充的颜色fillStyle)
- strokeRect(x, y, w, h):在位置(x, y)处以宽为w,高为h绘制一个矩形边框。(需要设置描边的属性strokeStyle等)。
- clearRect(x, y, w, h):在位置(x, y)处以宽为w,高为h,清除区域使其透明。
在canvas中绘图,可以利用所谓的绘图堆栈状态,每个状态都可以随时存储canvas上下文数据。
注意:当前路径和当前位图受canvas环境控制,不属于保存的状态。这个功能允许在画布上对单个对象进行绘画和制作动画。
旋转和平移变换
如果要将画布进行旋转等变换,就需要先将canvas画布设置为identity矩阵。
1 | context.setTransform(1, 0, 0, 1, 0, 0); |
旋转 rotate
在canvas中的角度都是弧度,所以旋转的角度也要换算为弧度。
1 | context.rotate(angle * Math.PI / 180); |
需要注意的是,需要在旋转后,再绘制图形。
1 | context.setTransform(1, 0, 0, 1, 0, 0); |
但是这样旋转后明显发现绘制的图形被旋转到了画布的外面。这是因为没有指定旋转的中心点,按照默认的点(0, 0)来进行的旋转。所以我们需要设置中心点。
注意:旋转会叠加。如果前面使用了旋转,那么后面的旋转会在前面的基础上进行旋转,比如,前面已经旋转了30度,后面要旋转60度,那么相对的只要再旋转30度就好了。
平移 translate
我们需要将原点平移到图形的中心点,这样对象才会围绕着自己转。那如何平移中心点到对象的中心点?
默认的中心点是(0, 0),我们需要绘制的对象的起点坐标为(100, 100),图形的宽高为200,我们计算中心点的话:
1 | var x = 100, y = 100, width = 200, height = 200; |
同时我们应用旋转:
1 | var x = 100, y = 100, width = 200, height = 200; |
有些图形是基本图形,类似于圆形、正方形,这种图形的中心点都比较好找,但是如果是不规则的图形呢?如何找中心点?
这需要用到边界框理论,任何形状的中心点都是半宽的x值和半高的y值。
缩放 scale
使用context.scale(x, y)
来可以对形状进行缩放,x代表水平缩放,y代表垂直缩放。
填充颜色以及渐变
canvas的着色有颜色和渐变两种。
基本颜色
可以使用html4规范的可以使用颜色字符串列表。比如有:
1 | 黑色 black #000000 |
所有这些颜色字符串可以直接使用到strokeStyle和fillStyle中。
当然也可以使用rgb()和rgba()设置填充色。rgba方法可以指定32位色值的填充色,其后8位表示透明度,范围为1(不透明)~0(透明)。
1 | context.fillStyle = 'black'; |
渐变
在画布上创建渐变有两个选项:线性或径向。线性渐变创建一个水平、垂直、或者对角线的填充图案。径向渐变自中心点创建一个放射状填充。
线性渐变
线性渐变有三种基本样式:水平、垂直、对角线。
首先需要创建一个线性渐变的载体:
1 | var gr = context.createLinearGradient(x0, y0, x1, y1); |
createLinearGradient方法调用中的4个值是开始渐变的左上角坐标和渐变结束的右下角坐标。
当创建一个水平渐变的时候,y值都是0.
当创建一个垂直渐变的时候,x值都是0.
对角渐变就是从左上角直到右下角。
定义了渐变的大小,就需要加入颜色断点:
1 | gr.addColorStop(offset, color); |
添加颜色断点的函数addColorStop的两个参数分别为:offset,距离开始位置的相对位置,范围为0~1;color,渐变的颜色。
然后应用到绘制的形状上:
1 | var gr = context.createLinearGradient(100, 0, 400, 0); |
不仅可以使用在填充色上,也可以使用在描边上。
径向渐变
径向渐变的过程和线性渐变非常类似。它们都采用颜色断点的概念来创建颜色变换。
同样需要先创建径向渐变的载体:
1 | var gr = context.createRadialGradient(x0, y0, r0, x1, y1, r1); |
这里createRadialGradient函数需要的留个参数其实是两个圆:圆心为(x0, y0)半径为r0的圆和圆心为(x1, y1)半径为r1的圆。第一个圆是开始圆,第二个结束圆。
然后像线性渐变一样设置颜色断点:
1 | gr.addColorStop(offset, color); |
言而总之,径向渐变就是从第一个圆心开始,进行颜色渐变过渡,到第二个圆的圆边结束。
渐变的效果具体要看这两个圆的相对位置。我们知道平面内的两个圆的位置有:相交、相切、相离。
这里有一个相离的效果图:
1 | var gr = context.createRadialGradient(x0, y0, r0, x1, y1, r1); |
阴影
可以使用下面四个参数给canvas添加阴影:
- shadowOffsetX 水平方向阴影,可以为正负
- shadowOffsetY 垂直方向阴影,可以为正负
- shadowBlur 设置阴影的模糊效果程度
- shadowColor 阴影的颜色
例如:
1 | context.fillStyle = 'black'; |
重新回顾了下canvas的基本绘图api,对一些旋转变换以及渐变和阴影做了补充。一步步了解后,一些不怎么熟悉的嫌麻烦不管涉及的模块也熟悉了,反而觉得理解更深入了一点,每天进步一点吧~😄
本文代码已同步github:
https://github.com/tony-yyj/canvas-game/tree/master/basic-draw