问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何用Canvas绘制多种图形

发布网友 发布时间:2022-04-23 15:06

我来回答

1个回答

热心网友 时间:2022-04-12 21:55

HTML5 的标准已经出来好久了,但是似乎其中的 Canvas 现在并没有在太多的地方用到。一个很重要的原因是,Canvas 的标准还没有完全确定,不适合大规模用在生产环境。但是,Canvas 的优点也是很明显的,例如在绘制含有大量元素的图表的时候,SVG 往往因为性能问题而无法胜任,例如我见过的一次技术分享会的抽奖环节,虽然效果比较炫,但因为每个头像都是 DOM,利用 CSS3 控制的动画,导致了性能非常低下。此外,随着硬件性能的提高,视频截图、图像处理等功能也逐渐可以在网页上实现了,大多数网站用的是 Flash,但是 Flash 在 Mac 电脑上性能不高,还需要学一些额外的知识。Canvas 则是直接使用 JavaScript 来进行绘图,对 Mac 友好,所以不失为 Flash 的一个继承者。

使用 Canvas
说了这么多,Canvas 究竟是个啥?
英文中 Canvas 的意思是“画布”,不过这里说的 Canvas 是 HTML5 中新出的一个元素,开发者可以在上面绘制一系列图形。Canvas 在 HTML 文件中的写法很简单:
<canvas id="canvas" width="宽度" height="高度"></canvas>

其中 id 属性是所有 HTML 元素都可以用的,Canvas 自带的属性只有后面两个(分别控制宽度、高度),没有其它的了。至于兼容性,CanIUse 上面写了,基础的功能目前用户使用的 90% 的浏览器都支持,所以大部分情况下还是可以放心使用的。

注意,一定要使用 Canvas 自带的 width 和 height 属性,不要使用 CSS 来控制,因为 CSS 控制会导致 Canvas 变形。可以试着与 PhptpShop 对比一下,后者是改变“图像大小”,前者才是正确的改变“画布大小”。例如下图是三张图片的横向拼接:最左边的黑框中是大小为 50px * 50px 的原图;中间是改变了图像大小为 100px * 100px 的效果,图像变得模糊,但是对于图像本身来说坐标范围并没有变大;最右边才是正确的 100px * 100px 的 Canvas。

Canvas 绝大部分的绘图方法都与 <canvas> 标签无关,需要使用 JavaScript 对其进行操作,这就是所谓的 Canvas API。
我们首先获取到这个元素:
var canvas = document.getElementById('canvas');

然后通过一个方法来获取可以调用一切 Canvas API 的入口:
var ctx = canvas.getContext('2d');

看到 2d 是不是很激动地联想到有没有 3d 呢?没有 3d 的写法,不过如果想要开启 3D 世界的大门,则可以写 canvas.getContext('webgl')。然而 WebGL 是基于 OpenGL ES 2.0 的一套标准,与本文是彻彻底底的两条路,因此这里就不讨论了。
Canvas 中的基本概念
坐标
与数学上常见的笛卡尔坐标系不太相同,Canvas 的坐标系是计算机中常见的坐标系,它长这样:

画布的最左上角是 (0,0),往右 x 增大,往下 y 增大,而且 x 和 y 都是整数(就算在计算过程中不是整数,在绘制的时候也会当作整数处理),单位是像素。
绘图
带大家怀旧一下。不知道有多少同学小时候玩过 logo 语言,在里面你可以控制一只小海龟在一块板子上行走、画画、提笔、落笔。Canvas 中也一样,你需要控制一只画笔的移动和绘制。然而 Canvas 更高级一些,你可以直接利用一些函数来画图,不用去控制那只画笔的位置。
Canvas 中的基本图形
通过上文定义的 ctx 变量可以干许多有意思的事情,我们先看看如何绘制一些基本图形。
线条
我们指定画笔移动到某一点,然后告诉画笔需要从当前这一点画到另一点。我们可以让画笔多次移动、绘制,最后统一输出到屏幕上。例子如下:
ctx.moveTo(10, 10);
ctx.lineTo(150, 50);
ctx.lineTo(10, 50);
ctx.moveTo(10, 20);
ctx.lineTo(40, 70);
ctx.stroke();

上面的代码中,lineTo 是产生线条用的函数,执行完之后画笔就移到了线条的终点。需要注意的是,线条此时并没有显示在屏幕上,必须调用 stroke 才会显示。这样设计是有道理的,因为向屏幕上输出内容需要耗费大量的资源,我们完全可以先攒够一波 lineTo,最后用 stroke 放一个大的。
路径
绘制路径非常简单,只需要先告诉 ctx 一声“我要开始画路径了”,然后通过各种方法(例如 lineTo)绘制路径。如果需要画一个封闭路径,那就最后告诉 ctx一声:“我画完了,你把它封闭起来吧。”当然,不要忘记利用 stroke 输出到屏幕上。
一个简单的例子:
ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(150, 50);
ctx.lineTo(10, 50);
ctx.closePath();
ctx.stroke();

如果我不想只描绘路径线条,而是想填充整个路径呢?可以将最后一行的 stroke 改成 fill,这样就跟使用了画图中的油漆桶一样,封闭路径里面的内容就都被填充上颜色了:
ctx.fill();

弧 / 圆形
绘制弧的函数参数比较多:
ctx.arc(圆心 x 坐标, 圆心 y 坐标, 半径, 起始角度, 终止角度, 是否为逆时针);

注意,在 Canvas 的坐标系中,角的一边是以圆心为中心的水平向右的直线。角度单位均为弧度。例如下图,确定了圆心、起始角度(图中标明的锐角)和终止角度(图中标明的钝角),方向为逆时针,于是就有了这么一个弧。如果方向为顺时针,那么就会是一个跟它互补的、非常非常大的弧……

所以如果转了 2π 圈之后,弧就成了圆形,因此也可以使用绘制弧的方式来绘制圆形:
ctx.beginPath();
ctx.arc(圆心 x 坐标, 圆心 y 坐标, 半径, 0, Math.PI * 2, true);
ctx.closePath();

最后一个参数随便填(当然也可以不填),因为不管是顺时针还是逆时针,转了 2π 圈之后都是一个圆。
矩形
如果只是想绘制一个横平竖直的矩形,可以使用下面的两个方法:
// 只描边
ctx.strokeRect(左上角 x 坐标, 左上角 y 坐标, 宽度, 高度);
// 只填充
ctx.fillRect(左上角 x 坐标, 左上角 y 坐标, 宽度, 高度);

线条样式 / 填充样式
之前绘制的所有图形都是黑色的,但是 Canvas 肯定不止这么一种颜色(不然标准的制定者会被喷的很惨)。事实上,Canvas 可以单独设置线条样式和填充样式,分别使用的是 strokeStyle 和 fillStyle。可能的值有三种:纯色、渐变、图像。既然线条样式与填充样式的使用方法相同,那么下面统一以填充样式为例。如果想设置线条样式,直接将所有的 fillStyle改成 strokeStyle 即可,里面的参数都不变。
/* 纯色填充 */
// 普通的颜色
ctx.fillStyle = '#0000ff';
// 带有透明度的颜色
ctx.fillStyle = 'rgba(64, 0, 127, 0.5)';

/* 渐变填充 */
// 设置渐变的尺寸(参数分别为起始点的 x 和 y、终止点的 x 和 y)
var gradient = ctx.createLinearGradient(0, 0, 170, 0);
// 设置过渡色,第一个参数是渐变的位置,第二个参数是颜色
gradient.addColorStop(0, 'magenta');
gradient.addColorStop(0.5, 'blue');
gradient.addColorStop(1.0, 'red');
// 设置填充样式
ctx.fillStyle = gradient;

/* 图片填充 */
// 创建图片
var image = new Image;
image.src = '/path/to/image.png';
// 创建图片笔触,可以指定图片的平铺方式,这里是横向平铺
var pattern = ctx.createPattern(image, 'repeat-x');
// 设置笔触填充
ctx.fillStyle = pattern;

关于渐变,除了代码中提到的线性渐变以外,还有 createRadialGradient,也就是径向渐变。
设置完填充样式之后,就可以使用 fill 来填充啦!如果设置的是线条样式,那么就可以使用 stroke 来描边。
当然,对于线条样式,还有个额外的方法叫 lineWidth 可以用来控制线条的宽度。
文字
要想在画布上画文字,首先需要知道所使用的字体和字号:
ctx.font = '30px Verdana';

然后就可以通过 strokeText 或者 fillText 来对字体描边或者填充字体。
ctx.strokeText("Hello Coding!", 23, 33);
ctx.fillText("Hello Coding!", 23, 66);

图片
在 Canvas 中绘制图片有三种方法:
// 指定绘制位置
ctx.drawImage(image, x, y);
// 指定绘制位置和图像宽高
ctx.drawImage(image, x, y, width, height);
// 指定剪裁区域、绘制位置和图像宽高
ctx.drawImage(image, sx, sy, swidth, sheight, x, y, width, height);

参数的含义依次如下:
image: 要使用的 Image、Canvas 或 Video
sx: 可选,开始剪切的 x 坐标
sy: 可选,开始剪切的 y 坐标
swidth: 可选,被剪切图像的宽度
sheight: 可选,被剪切图像的高度
x: 在画布上放置图像的 x 坐标
y: 在画布上放置图像的 y 坐标
width: 可选,要使用的图像的宽度
height: 可选,要使用的图像的高度

画布设置
细心的同学可能会发现,刚才有些属性是直接对 ctx 变量做设置,例如 ctx.lineWidth,只要设置了它,那么后续画出来的线条全都是这么个宽度。
其实,Canvas 的设置项还有许多,例如我们可以直接移动画布、旋转画布、设置全局的绘制透明度等等。这些设置还可以随时保存和恢复。
要注意的一点是,所有已经画在画布上的东西,是已经定死了的,不管之后再次进行任何设置都不会再改变。这个很像 Windows 下的画图程序。
废话不多说,直接上代码:
// 移动画布,其实就是移动坐标系
ctx.translate(往右移动的量, 往下移动的量);
// 旋转画布,旋转中心为坐标系原点
ctx.rotate(顺时针旋转的角度);
// 以坐标系原点为中心缩放画布
ctx.scale(横向放大倍数, 纵向放大倍数);
// 设置绘制透明度,如果 fillStyle 等属性设置了透明度则会叠加
ctx.globalAlpha(零到一的小数);
// 设置全局组合操作
ctx.globalCompositeOperation = 'lighter';
// 保存当前设置
ctx.save();
// 恢复上次保存的设置
ctx.restore();

移动、旋转、缩放其实就是在控制绘图的坐标系,如果你在调用这三个方法的时候,脑子里时刻有一个带刻度的坐标系,效果会非常好。
事实上,Canvas 的坐标变换遵循计算机图形学的知识:变换矩阵。简单来说,一个坐标可以看成是一个矩阵,坐标所对应的矩阵乘上变换矩阵就可以实现对坐标的变换。为了提升计算的效率,可以先计算出几种变换复合之后的变换矩阵,然后直接通过 transform 函数对当前坐标系进行变换,或者通过 setTransform 函数将坐标系重置为初始状态后再进行变换。至于变换矩阵的内容,对于本文来说就有些超纲了。
全局组合操作有点像 PhotoShop 里面的“混合选项”,具体的实现方式还没有完全确定,目前常见浏览器都统一了的实现方式有:source-over、source-atop、destination-over、destination-out、lighter、xor。具体的行为可以看 Mozilla 官方文档,但是由于标准还未完全确定,因此其它浏览器不保证所有的行为都跟 Mozilla 的标准一致。一般来说,比较常见的是 source-over 和 lighter 两种,这两种的标准在浏览器界也算是无可争议的。
至于保存和恢复设置就有点好玩了,首先需要了解一个叫“栈”的东西。
栈是一个一维数组,规定只能从一个方向操作。栈一开始是空的,我们可以从这个方向往数组 push 元素,也只能从这个方向把最后一个元素(栈顶元素)pop 出来,除此以外没有任何多余的操作。当然,pop 的次数不能多于 push 的次数,因为 pop 到栈底的时候栈里就已经没有元素了,此时再 pop 是没有意义的。栈的用处有很多,例如括号匹配、表达式求值、深度优先搜索,甚至绝大部分语言的函数调用都要用到栈。
每次我们调用 save 函数,实际上是将当前的全局设置 push 到了一个专门栈上,每次调用 restore 函数的时候将最后一次保存的内容 pop 出来并用它覆盖当前的全局设置,这样栈顶就是最近一次保存的内容了。保存和恢复在某些情况下很好用,例如我需要画一个歪着的图形,然后继续画正着的图形,这样就可以先调用 save,然后调用 rotate,画完图形之后再 restore 回来,继续画其它的图形。
其实 Canvas 还有许多方法,例如 toDataURL 直接将当前画布上的内容转换为十六进制的 data-url,getImageData直接将图像转换为 RGBA 数组以供图像处理算法使用,putImageData 将 RGBA 数组转换为图片显示在画布上等等。如果配上 JavaScript 的定时更新(最好用 requestAnimationFrame 而不是 setInterval),则可以产生动画效果。网上还有许多 Canvas 的库,可以让程序员更简便地基于 Canvas 编写属于自己的特效或功能。在这儿我想说一句话:大家的脑洞有多大,Canvas 的能力就有多强~
如何用Canvas绘制多种图形

绘制路径非常简单,只需要先告诉 ctx 一声“我要开始画路径了”,然后通过各种方法(例如 lineTo)绘制路径。如果需要画一个封闭路径,那就最后告诉 ctx一声:“我画完了,你把它封闭起来吧。”当然,不要忘记利用 stroke 输出到屏幕上。一个简单的例子:ctx.beginPath();ctx.moveTo(10, 10);ctx....

canvas使用图像(绘制图、图像平铺、图像剪裁、像素处理、绘制文字)

绘制文字的字体由font属性来控制。如果你需要使用颜色来填充文字或制作描边文字,可以使用fillStyle和strokeStyle属性来完成。要在canvas上绘制文字,可以通过的fillText()函数或strokeText()函数来完成。表示在(x,y)的位置,绘制text的内容。可选参数maxWidth为文本的最大宽度,单位为像素。如果设置了该属性,...

canvas同时绘制多个形状,为什么不显示

需要关闭硬件加速。Canvas中文名称叫“画布”,它是游戏中所有UI组件的“容器”。一个场景中,可以允许多个Canvas对象的存在,还允许Canvas之间可以进行“嵌套”使用。Canvas绘制多个图形不显示需要关闭硬件加速,有三种方式。1、在Manifest的application中设置。2、在Manifest的Activity中设置。3、在目标View上...

canvas制作海报-如何用python画五一海报

1.首先一样,获取Canvas对象:2.获取像素比,将Canvas宽高进行放大,放大比例为:devicePixelRatio/webkitBackingStorePixelRatio,我们写了一个兼容的方法。3.按实际渲染倍率来缩放canvas。注意基础知识点:要设置canvas的画布大小,使用的是和;要设置画布的实际渲染大小,使用的style属性或CSS设置的width和height,只...

如何使用HTML5 Canvas动态的绘制拓扑图

1.添加引用\r\n右击项目-添加引用-浏览 找到本地的dll文件\r\n2.using 该dll文件里面代码的名称空间 \r\n然后就可以调用dll文件里面的类和方法 &lt;canvas&gt;定义和用法:Canvas 对象表示一个 HTML 画布元素 -&lt;canvas&gt;。它没有自己的行为,但是定义了一个 API 支持脚本化客户端绘图操作。你可以直接在...

使用canvas绘制“实心”矩形时,使用什么方法,该方法的参数分别代表什么...

和 VML 使用一个 XML 文档来描述绘图。这两种方式在功能上是等同的,任何一种都可以用另一种来模拟。从表面上看,它们很不相同,可是,每一种都有强项和弱点。例如,SVG 绘图很容易编辑,只要从其描述中移除元素就行。要从同一图形的一个 &lt;canvas&gt; 标记中移除元素,往往需要擦掉绘图重新绘制它。

如何使用HTML5 CANVAS绘制渐变色

我们可以通过2D上下文的createLinearGradient()方法来创建一个线性渐变。下面是一个例子:var canvas = document.getElementById(ex1);var context = canvas.getContext(2d);var x1 = 0;var y1 = 0;var x2 = 100;var y2 = 0;var linearGradient1 = context.createLinearGradient(x1,y1,x2,y2...

canvas简介以及常用性能优化

HTML5 的 Canvas 元素使用 JavaScript 在网页上绘制图像。 画布是一个矩形区域,您可以控制其每一像素。 Canvas 拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法。 Canvas 是逐像素进行渲染的。 在 Canvas 中,一旦图形被绘制完成,它就不会继续得到浏览器的关注。如果其位置发生变化...

如何使用js在画布上绘制图形

创建画布在介绍如何绘制图形前,我们首先要使用&lt;canvas&gt;元素创建一个画布:&lt;canvas id="myCanvas" width="300" height="200" style="border:2px solid red;"&gt;当前的浏览器不支持HTML5 canvas标记。&lt;/canvas&gt;设置画布的大小为:宽400px、高200px,添加一个2px的红色、实线边框;在给创建好的画布一...

如何在 HTML5 画布上绘图

通过闭合对 beginPath 和 closePath 调用中的多个线条,可以从上述线条的任意组合中绘制一个 2D 形状。然后,整个形状可以使用 fill 接收一种颜色。前面设置的笔划样式将创建白色线条,在与应用于主体的红色 (#f00) 合并时,该形状将继承双色调外观。 // Draw a red diamond that spans the entire canvas.myContext...

使用canvas绘制图形 使用canvas绘制图形步骤 如何用钢笔工具绘制图形 如何用c语言绘制图形 用图形绘制美丽的图案 怎么用ps绘制图形 绘制图形用什么软件 用MySQL绘制图形 绘制普通图形用什么软件
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
颐莲洗面奶是氨基酸洗面奶么 颐莲洗面奶适合什么肤质 西天取经有哪些神仙 西天真的有什么神仙 在英语中L表示"左", R表示"右"。 我有套房子要装修,想清包工,现需要一份材料报告及价格。急·详细的... 该怎么挑选小孩子戴的口罩呀,听周围朋友说他们都是买稳健医疗的儿童口罩... cpb洗面奶适合什么肤质 ?cpb洗面奶痘痘肌肤洗了脸痛 cpb洗面奶和香奈儿洗面奶哪个好 滋润型和清爽型怎么挑选 物流一立方算多少kg 氯化钾,生石灰,浓盐酸,碳酸钙是否能够与水发生反应 王子策划工作室都有哪些业务?听说他们还有个中国企业商务精英微群和中国营销策划&媒体人群,号码是多少啊 人和网的大事件 如何实现分享网站文章到微信朋友圈时显示指定缩略图或LOGO 同性恋亲友会的发展历史 超越丰田/追赶马自达? 比亚迪如何做出热效率达43%的发动机 可以作为社会实践分享会的礼品有什么? 网站文章分享到微信时如何显示指定的Logo? 海信平板电脑10寸E22怎么打开我的网络? eviews面板数据相关性结果分析求解释 这个结果麻烦帮我说明一下是什么意思 谢谢 联想e430c笔记本电脑触摸板怎么关 thinkpad笔记本e系列的触摸板怎么打开 PS-4+E345323这是什么主板? 电脑启动出现主板界面,显示CPU FAN IS EORR! 要按F1才能进入系统。。 上海股权托管交易中心e板挂牌,E是什么的缩写? E173873电源板 学生用什么笔记本电脑好 性价比高的学生笔记 求推荐笔记本,中等厚度,分辨率高,处理速度快,重点是质量要好,价格5000左右 家里已经有台式液晶的电脑了,配置很好,现在想买个便宜的笔记吧,可以挂QQ,开网页就可以,不玩游戏 求推荐便宜点的触屏笔记本,最好4000块以内的,要性能好一点的,麻烦给介绍下。 什么牌子的笔记本电脑又便宜又好用 歌词含各种颜色的歌有哪些? 如何建立企业文化? 我在路边发现一家店得LOGO好看 我临摹下来 发在公众号上分享 这个行为有没有对该商标侵权? 香港有云服务器么???是真正的云服务器 不是vps哦 如何购买香港云服务器 有没有国内外都能快速访问的香港云服务器 香港云服务器有哪些特点和优势 香港服务器和香港云服务器哪个好啊? 上海医保卡办理地点 上海金山社保与医保在哪里办理 上海闵行区办理社保卡需要去哪里 我是上海金山区的,社保卡遗失在哪补? 崇明社保中心在哪里 上海 社保卡在哪里更换 金山区开户的公司哪里领社保医保卡 金山医保卡在哪领取 仓山区金山医保补办 外地人在上海交保险社保卡怎么办理? 我要去办理社保卡。。请问上海社保局的电话和地址是多少 金山石化医保卡在哪里