博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何用 Canvas绘制图形
阅读量:6954 次
发布时间:2019-06-27

本文共 6412 字,大约阅读时间需要 21 分钟。

绘制矩形

不同于SVG,HTML中的元素canvas只支持一种原生的图形绘制:矩形。所有其他的图形的绘制都至少需要生成一条路径。不过,我们拥有众多路径生成的方法让复杂图形的绘制成为了可能。

首先,我们回到矩形的绘制中。canvas提供了三种方法绘制矩形:

fillRect(x, y, width, height)
绘制一个填充的矩形
strokeRect(x, y, width, height)
绘制一个矩形的边框
clearRect(x, y, width, height)
清除指定矩形区域,让清除部分完全透明。

上面提供的方法之中每一个都包含了相同的参数。x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。width和height设置矩形的尺寸。

下面的draw() 函数是前一页中取得的,现在就来使用上面的三个函数。

矩形(Rectangular)例子

function draw() { var canvas = document.getElementById('canvas'); if (canvas.getContext) { var ctx = canvas.getContext('2d') ctx.fillRect(25,25,100,100); ctx.clearRect(45,45,60,60); ctx.strokeRect(50,50,50,50); } }

接下来我们能够看到clearRect()的两个可选方法,然后我们会知道如何改变渲染图形的填充颜色及描边颜色。

不同于下面所要介绍的路径函数(path function),以上的三个函数绘制之后会马上显现在canvas上,即时生效。

绘制路径

使用路径绘制图形需要一些额外的步骤。首先,需要生成路径。然后再路径上使用绘图命令绘制。之后闭合路径。一旦路径生成,你就能通过描边或填充路径来渲染图形。以下是所要用到的函数:

beginPath()
新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。
closePath()
闭合路径之后图形绘制命令又重新指向到上下文中。
stroke()
通过线条来绘制图形轮廓。
fill()
通过填充路径的内容区域生成实心的图形。

生成路径的第一步叫做beginPath()。本质上,路径是由很多子路径构成,这些子路径都是在一个列表中,所有的子路径(线、弧形、等等)构成图形。而每次这个方法调用之后,列表清空重置,然后我们就可以重新绘制新的图形。

 注意:当前路径为空,即调用beginPath()之后,或者canvas刚建的时候,第一条路径构造命令通常被视为是moveTo(),无论最后的是什么。出于这个原因,你几乎总是要在设置路径之后专门指定你的起始位置。

第二步就是调用函数指定绘制路径

第三,就是闭合路径closePath(),不是必需的。这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。

注意:当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合

绘制一个三角形

例如,绘制三角形的如下:

function draw() { var canvas = document.getElementById('canvas'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); ctx.beginPath(); ctx.moveTo(75,50); ctx.lineTo(100,75); ctx.lineTo(100,25); ctx.fill(); } }

移动笔触

一个非常有用的函数,而这个函数实际上并不能画出任何东西,也是上面所描述的路径列表的一部分,这个函数就是moveTo()。或者你可以想象一下在纸上作业,一支钢笔或者铅笔的笔尖从一个点到另一个点的移动过程。

moveTo(x, y)
将笔触移动到指定的坐标x以及y上。

当canvas初始化或者beginPath()调用后,你通常会使用moveTo()函数设置起点。我们也能够使用moveTo()绘制一些不连续的路径。看一下下面的例子。我将用到moveTo()方法

function draw() { var canvas = document.getElementById('canvas'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); ctx.beginPath(); ctx.arc(75,75,50,0,Math.PI*2,true); // 绘制 ctx.moveTo(110,75); ctx.arc(75,75,35,0,Math.PI,false); // 口(顺时针) ctx.moveTo(65,65); ctx.arc(60,65,5,0,Math.PI*2,true); ctx.moveTo(95,65); ctx.arc(90,65,5,0,Math.PI*2,true) ctx.stroke(); } }

如果你想看到连续的线,你可以移除调用的moveTo()。

线

绘制直线,需要用到的方法lineTo();

lineTo(x, y)
绘制一条从当前位置到指定x以及y位置的直线。

该方法有两个参数:x以及y ,代表坐标系中直线结束的点。开始点和之前的绘制路径有关,之前路径的结束点就是接下来的开始点,等等。。。开始点也可以通过moveTo()函数改变。

下面的例子绘制两个三角形,一个是填充的,另一个是描边的。

function draw() { var canvas = document.getElementById('canvas'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); // 填充三角形 ctx.beginPath(); ctx.moveTo(25,25); ctx.lineTo(105,25); ctx.lineTo(25,105); ctx.fill(); // 描边三角形 ctx.beginPath(); ctx.moveTo(125,125); ctx.lineTo(125,45); ctx.lineTo(45,125); ctx.closePath(); ctx.stroke(); } }
 

这里从调用beginPath()函数准备绘制一个新的形状路径开始。然后使用moveTo()函数移动到目标位置上。然后下面,两条线段绘制后构成三角形的两条边。

你会注意到填充与描边三角形步骤有所不同。正如上面所提到的,因为路径使用填充(filled)时,路径自动闭合,使用描边(stroked)则不会闭合路径。如果没有添加闭合路径closePath()到描述三角形函数中,则只绘制了两条线段,并不是一个完整的三角形。

圆弧(Arcs)

绘制圆弧或者圆,我们使用arc()方法。当然可以使用arcTo(),不过这个的现实并不是那么的可靠,所以我们这里不作介绍。

arc(x, y, radius, startAngle, endAngle, anticlockwise)
绘制圆弧

该方法有五个参数: x,y为绘制圆弧所在圆上的圆心坐标。radius为半径。startAngle以及engAngle参数用弧度定义了开始以及结束的弧度。这些都是以x轴为基准。参数anticlockwise 为一个布尔值。为true时,是逆时针方向,否则顺时针方向。

注意:arc()函数中的角度单位是弧度,不是度数。角度与弧度的表达式:radians=(Math.PI/180)*degrees。

下面的例子比上面的要复杂一下,下面绘制了12个不同的角度以及填充的圆弧。

下面两个for循环,生成圆弧的行列(x,y)坐标。每一段圆弧的开始都调用beginPath()。代码中,每个圆弧的参数都是可变的,实际生活中,我们并不需要这样做。

x,y坐标是可变的。半径(radius)和开始角度(startAngle)都是固定的。结束角度(endAngle)在第一列开始时是180度(半圆)然后每列增加90度。最后一列形成一个完整的圆。

clockwise 语句作用于第一、三行是顺时针的圆弧,anticlockwise作用于二、四行为逆时针圆弧。if语句让一、二行描边圆弧,下面两行填充路径。

function draw() { var canvas = document.getElementById('canvas'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); for(var i=0;i<4;i++){ for(var j=0;j<3;j++){ ctx.beginPath(); var x = 25+j*50; // x 坐标值 var y = 25+i*50; // y 坐标值 var radius = 20; // 圆弧半径 var startAngle = 0; // 开始点 var endAngle = Math.PI+(Math.PI*j)/2; // 结束点 var anticlockwise = i%2==0 ? false : true; // 顺时针或逆时针 ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise); if (i>1){ ctx.fill(); } else { ctx.stroke(); } } } } }
 

贝塞尔(bezier)以及二次贝塞尔

下一个十分有用的路径类型就是 。二次以及三次贝塞尔曲线都十分有用,一般用来绘制复杂有规律的图形。

quadraticCurveTo(cp1x, cp1y, x, y)
绘制二次贝塞尔曲线,x,y为结束点,cp1x,cp1y为控制点。
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
绘制三次贝塞尔曲线,x,y为结束点,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二。

使用canvas来绘制图形右边的图能够很好的描述两者的关系,二次贝塞尔曲线有一个开始、结束点(蓝色)以及一个控制点(红色),而三次贝塞尔曲线使用两个控制点。

参数x、y在这两个方法中都是结束点坐标。cp1x,cp1y为坐标中的第一个控制点,cp2x,cp2y为坐标中的第二个控制点。

使用二次以及三次贝塞尔曲线是由一定的难度的,因为不同于像Adobe Illustrators这样的矢量软件,我们所绘制的曲线没有直接的视觉反馈给我们。这让绘制复杂的图形十分的困难。在下面的例子中,我们会绘制一些简单有规律的图形,如果你有时间,以及更多的耐心很多复杂的图形你都可以绘制出来。

下面的这些例子没有多少是困难的。这两个例子中我们会连续绘制贝塞尔曲线,最后会形成复杂的图形。

二次贝塞尔曲线

这个例子使用多个贝塞尔曲线来渲染对话气泡。

function draw() { var canvas = document.getElementById('canvas'); if (canvas.getContext) { var ctx = canvas.getContext('2d'); // Quadratric curves example ctx.beginPath(); ctx.moveTo(75,25); ctx.quadraticCurveTo(25,25,25,62.5); ctx.quadraticCurveTo(25,100,50,100); ctx.quadraticCurveTo(50,120,30,125); ctx.quadraticCurveTo(60,120,65,100); ctx.quadraticCurveTo(125,100,125,62.5); ctx.quadraticCurveTo(125,25,75,25); ctx.stroke(); } }
 

三次贝塞尔曲线

这个例子使用贝塞尔曲线绘制心形。

function draw() { var canvas = document.getElementById('canvas'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); // Quadratric curves example ctx.beginPath(); ctx.moveTo(75,40); ctx.bezierCurveTo(75,37,70,25,50,25); ctx.bezierCurveTo(20,25,20,62.5,20,62.5); ctx.bezierCurveTo(20,80,40,102,75,120); ctx.bezierCurveTo(110,102,130,80,130,62.5); ctx.bezierCurveTo(130,62.5,130,25,100,25); ctx.bezierCurveTo(85,25,75,37,75,40); ctx.fill(); } }
 

矩形

直接在画布上绘制矩形的三个额外方法,正如我们开始所见的 ,同样,也有rect()方法,将一个矩形路径增加到当前路径上。

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

当该方法执行的时候,moveTo()方法自动设置坐标参数(0,0)。也就是说,当前笔触自动重置会默认坐标。

组合使用

function draw() { var canvas = document.getElementById('canvas'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); roundedRect(ctx,12,12,150,150,15); roundedRect(ctx,19,19,150,150,9); roundedRect(ctx,53,53,49,33,10); roundedRect(ctx,53,119,49,16,6); roundedRect(ctx,135,53,49,33,10); roundedRect(ctx,135,119,25,49,10); ctx.beginPath(); ctx.arc(37,37,13,Math.PI/7,-Math.PI/7,false); ctx.lineTo(31,37); ctx.fill(); for(var i=0;i<8;i++){ ctx.fillRect(51+i*16,35,4,4); } for(i=0;i<6;i++){ ctx.fillRect(115,51+i*16,4,4); } for(i=0;i<8;i++){ ctx.fillRect(51+i*16,99,4,4); } ctx.beginPath(); ctx.moveTo(83

转载于:https://www.cnblogs.com/zhoukui224466/p/4640969.html

你可能感兴趣的文章
环境配置就是安装软件,修改软件的配置文件,安装软件就是文件的复制,与新增--linux下一切皆文件...
查看>>
11.27号
查看>>
c++ STL模板(一)
查看>>
JAVA面向对象概述
查看>>
mysql触发器
查看>>
UVA 10441 Catenyms 欧拉道路
查看>>
wpf 进度条 下拉
查看>>
python之 模块与包
查看>>
x = cos x 的解析形式
查看>>
base64工具类
查看>>
UUIDGenerator
查看>>
NUC1931 Problem D 区间素数【素数筛选】
查看>>
webpack编译流程漫谈
查看>>
oracle锁
查看>>
Android攻略--单位转化器UC--Units Converter(学习笔记)
查看>>
js 的强制 类型 转换cast, 伪对象?
查看>>
jsfl 全选
查看>>
MySQL安装与配置
查看>>
为什么开发移动端web不使用jQuery
查看>>
查询结果列传行
查看>>