zl程序教程

您现在的位置是:首页 >  前端

当前栏目

《JavaScript高效图形编程(修订版)》——6.7 画布绘制基础

JavaScript基础编程 高效 绘制 图形 修订版 画布
2023-09-11 14:17:42 时间

本节书摘来自异步社区《JavaScript高效图形编程(修订版)》一书中的第6章,第6.7节,作者:【美】Raffaele Cecco著,更多章节内容可以访问云栖社区“异步社区”公众号查看

6.7 画布绘制基础

下面的九节将讨论基本的画布绘图命令。

6.7.1 画布元素
在网页中插入画布元素和插入任何其他HTML元素没有什么不同。

screenshot

如果你不指定任何宽度或高度属性,默认大小为300×150像素。可以但不推荐通过CSS(例如,宽度:50%)改变画布大小。输出有可能是被扭曲或被缩放的,这取决于浏览器的实现。但是,你可以用CSS设置边框、边距和背景颜色,虽然这绝不会影响绘制到画布内容本身。坐标系统默认左上角为原点(0,0),因此绘制在坐标(10,15)的图案将定位在从左往右第10像素,从上往下第15个像素。

如果浏览器不支持画布, 将显示开始和结束之间的替代内容(fallback content)。理想的情况下,替代内容应该是画布所显示数据常规的文本或HTML表示。例如,画布中可能显示饼图,替代内容会显示一个普通表格。有的情况下,替代内容根本无法取代画布;游戏和绘图应用程序没有对应的文本或HTML表示。在这种情况下,替代内容应显示一个有用的信息,向用户解释:画布不可用,浏览器应升级。

单独放到页面的画布没有给我们任何的功能,它必须由JavaScript控制才能做些有用的事。你很少会看到没有id属性的画布,因为JavaScript代码通常用id属性来识别画布。通常情况下,JavaScript将这样得到画布的“句柄”变量:

screenshot

6.7.2 绘图环境
我们必须从画布获得一个“绘图环境”后才可以使用绘图命令:

screenshot

虽然不是正式的推荐,不过在画布例子代码中你会经常看到用ctx来代表绘图环境。

提示:
画布还提供了一个3D绘图环境,使你可以访问目前处于试验阶段的WebGL接口。WebGL基于OpenGL ES 2.0的标准(OpenGL的削减版本),并通过JavaScript提供3D图形处理能力。在大多数浏览器的开发版本中都支持。OpenGL实际是一个的底层函数集,你仍然需要做大量的工作来创建一个3D应用程序。

在Web社区曾经有人怀疑JavaScript是否能在较复杂的3D场景中管理对象的层次;不管这些对象是不是由WebGL绘制,管理一个3D应用或游戏需要进行大量的计算。不过随着JavaScript性能的不断改善,大家对JavaScript越来越有信心,而且出现各种更高层的3D库,可以简化3D应用开发。所有这些库都是建立在WebGL之上的:

O3D(原本是一个插件,但现在是一个JavaScript库) SpiderGL SceneJS Processing.js

6.7.3 绘制矩形
画布内置的绘图形状非常有限,实际上只有矩形而已:

screenshot

不过这个限制不算大问题,因为我们可以用直线和曲线组合定义的路径来创建所有其他形状。

6.7.4 绘制直线和曲线的路径
路径定义可以填充和/或使用大纲描边的形状。画布包括以下功能执行路径绘制:
screenshot

需要注意的是,“to”命令(lineTo()、bezierCurveTo()等)的结束位置也定义了下一个“to”命令的开始位置。你可以将“to”命令想象成用笔在纸上连续地画线(不离开纸面)。moveTo()命令则使你将笔离开纸面,并从其他地方重新开始画。

下面的示例使用线在左上角绘制一个填充三角形和描边三角形(如图6-3所示),假设画布尺寸为500×500像素:

screenshot

注意你不需要为填充三角形执行closePath()命令,因为fill()自动关闭路径。

提示:
画布允许你指定分数像素位置。你可能觉得这很奇怪,因为像素是不能分割的单元元素。实际上画布是使用了抗锯齿技术给人以分数像素位置存在的假象。这可以使视觉上边缘更干净,移动更平滑,尤其当移动速度较慢时。
你可以使用arc()命令来绘制圆,或圆的部分:

screenshot

参数如下:

x,y

圆心位置。

radius

像素半径。

startAngle,endAngle

绘图将“横扫”这两个角度之间。角度以弧度定义,2π弧度(约6.283)相当于360°。

antiClockwise

绘制弧线的方向。

下面是弧度转换所需的计算:

screenshot

下面的代码绘制了两排圆,每个圆的开始角度为0弧度,endAngle逐渐增加。上一行以顺时针绘制,下一行以逆时针绘制(如图6-4所示)。

screenshot

提示:
如果没有使用 moveto()来设置开始位置,将从上个弧的结束位置开始画新的弧。
arcTo()命令和arc()命令类似,但是以不同的方式指定曲线:

screenshot

曲线由两条直线定义,第一条直线是从当前位置到第一个点(x1,y1),第二条直线是从点(x1,y1)到点(x2,y2)。这样定义一条曲线是为了方便创建直线之间的圆角。曲线将占据两条直线相交的角。

下面的函数绘制(w,h)大小的圆角矩形。圆角的半径由参数cr定义。

screenshot

图6-5显示了使用不同圆角半径(从0开始,依次增加2π弧度)调用此函数的结果。

screenshot

以下的页面代码显示了如何在循环内调用drawRoundedRect()给出图6-5所示的输出:

screenshot

quadtraticCurveTo()和bezierCurveTo()命令使我们绘制一个或两个控制点的曲线。控制点可以使曲线弯曲,从而得到arc()和arcTo()命令不能绘制的非对称曲线。这种类型的曲线经常可以在Photoshop、Freehand和Inkscape等矢量绘图工具中见到。在JavaScript中使用这些曲线可能比较棘手,因为我们不能直接看到控制点的位置和它们对曲线的效果。

以下页面代码分别在画布顶部和底部显示了二次曲线和贝塞尔曲线(如图6-6所示)。它还显示了可以用鼠标拖动的控制点,使用了jQuery UI的“可拖动”功能来移动控制点。请注意控制点实际上是普通的div元素,而不是画布路径。以这种方式组合画布和普通DOM元素不仅完全合法,而且非常有用:

screenshot

screenshot

screenshot

6.7.5 绘制位图图像
我们可以用drawImage()命令绘制位图图像。这个命令可以有3、5或9个参数。在所有情况下,第一个参数指定图像源以提供绘制的像素数据。图像源可以是用image()函数载入的图像、普通的标签、甚至是另一个画布或

screenshot

警告:
如果使用drawImage()时遇到性能问题,确保图像源是另一个画布标签可能是有益的。这防止了某些浏览器上的图像转换开销。例如,图6-7中的视频“爆炸”效果将视频图像复制到画布元素,再使用drawImage()分为小片。
screenshot

3个参数版本的drawImage()最容易使用,它只简单将图像源复制到画布的(x,y)坐标。位图的宽度和高度由源位图本身决定:

screenshot

5个参数版本允许你指定目标高度和宽度,使你能够缩放图像到所需的大小:

screenshot

9个参数版本允许你复制图像源的一部分,其中参数2~5指定源图像中的源矩形块,参数6~9指定内绘制在画布上的目标矩形:

screenshot

警告:
如果你使用 drawImage()分数像素位置,有些浏览器(特别是Firefox和Opera)可能遭受严重的性能损失和其他奇怪的故障。为了避免这些问题,确保将位置四舍五入为整数形式:
screenshot

screenshot

6.7.6 颜色、描边和填充
在前面的例子中,我们使用了stroke()命令来创建一个默认黑色的1像素宽的路径轮廓。你可以使用lineWidth和strokeStyle属性更改轮廓的风格,并用fillStype属性指定内部填充颜色。下面是一个加入这些属性的圆角矩形代码(如图6-9所示):

screenshot

请注意描边比指定的4个像素薄。这是因为描边以路径为中心,而内部的两个项目被绿色填充隐藏了。增加线条的宽度才能获得所期望的结果。

你还可以同alpha值指定颜色的透明度。alpha值的范围从0(完全透明)到1(完全不透明)。除了为当前描边或填充命令设定本地alpha值外,你还可以使用globalAlpha属性给所有描边和填充设置alpha值;本地alpha值将被乘以globalAlpha属性。

此外,你还可以用globalApha属性给位图设置透明度。位图中所有像素的alpha值将被乘以globalAlpha属性。PNG图像包含了一个alpha通道实现透明效果,图像中alpha为0.5的像素,用globalAlpha为0.5绘制将实际得到alpha为0.25。

警告:
绘制alpha值小于1的元素将涉及浏览器额外的工作,因为浏览器必须进行额外的计算来显示每个像素的最终颜色。无论画布实现是否使用硬件加速都是如此。当设计你的应用程序时,考虑是否绝对需要使用alpha值,尤其是当绘制速度很重要时。

如果指定(或通过globalAlpha计算得到)alpha值为0(完全透明),浏览器可能仍然会尝试绘制。这涉及不必要的工作,可能带来性能问题。尽量避免画alpha值为0的元素。
我们用CSS3定义画布中的颜色。下面这些声明语句中的任意一条都可以设定填充颜色为红色:

screenshot

除了纯色的填充和描边外,你可以使用createLinearGradient()或createRadial Gradient() 命令指定颜色渐变。

提示:
用createLinearGradient()创建渐变,需要一些设置:

1.使用createLinearGradient()创建一个CanvasGradient对象。传入的4个参数定义了将绘制渐变颜色的线。

2.沿着这条线添加颜色点,其中0表示线的开始,1表示线的末尾。定义渐变你必须至少设置两个颜色点。

3.使用CanvasGradient对象作为填充或描边的样式。
我们使用CanvasGradient addColorStop()命令添加颜色点。此命令接受0和1之间的值,其中0表示渐变的开始,1代表结束。下面的代码定义了一个从黑色到白色再到红色的渐变:

screenshot

下面的函数产生一个渐变的天空和草地效果(如图6-10所示):
screenshot

我们调用这个函数时要传入一个画布环境。

如果绘制的矩形和画布的大小不同,会发生什么?我们将前面的函数最后一行替换为下面一行,使得图6-11显示了1/4个画布大小的矩形。
screenshot

请注意绘制矩形的行为像在CanvasGradient对象定义的渐变上开一个“窗口”。

createRadialGradient()命令则可以创建一个跨越两个圆的径向渐变。该命令接受指定圆心和半径的两个圆:

screenshot

通常情况下,两个圆心在相同位置,而且第一个圆在第二个圆之内。内圆的所有区域都用addColorStop()定义的第一种颜色填充;这种颜色将渐变到由addColorStop()定义的最终颜色,并填充从内圆到外圆的区域。而外圆外的区域也是由addColorStop()定义的最终颜色填充。

下面的函数创建一个太阳,使用径向渐变,纯白色变淡黄色透明。在天空渐变和草地渐变上放上这个太阳,即可得到一个阳光明媚的效果(如图6-12所示):

screenshot


带你读《小创客趣玩micro bit开发板编程》之二:JavaScript Blocks 图形编程 本书从 micro:bit介绍、micro:bit开发方式、Makecode图形化编程和创意编程案例实践4个维度带领大家逐步理解和学习 micro:bit的使用方法。
《JavaScript高效图形编程(修订版)》——导读 JavaScript结合了HTML5 Canvas等特性,给Web开发人员提供了真正可以不用Adobe Flash等插件的方案。而WebGL等特性则为使用JavaScript和浏览器进行图形编程描绘了非常美好的未来。
《JavaScript高效图形编程(修订版)》——6.11 一个图形使用画布的WebSockets聊天应用 HTTP数据携带了大量的头信息。请求一个字节的数据可能会导致发送数百个字节的、额外的、“看不见的”头信息也被发送。头信息通常包含被传送数据的性质,如内容类型、缓存、编码等。
《JavaScript高效图形编程(修订版)》——6.10 用画布sprites取代DHTMLsprite 我们尽量将绘制sprites的细节“隐藏”在DHTMLSprite对象中,使得应用程序可以很容易实现和使用一个不同的sprite系统。现在我们将在演示中使用一个新的CanvasSprite对象,它利用了性能更强的画布元素。
《JavaScript高效图形编程(修订版)》——6.9 画布和递归绘图 下面我们将尝试用更有趣的东西,具体说是用一点三角函数和一点随机元素,来代替简单的递归递减。图6-14显示了递归调用简单的画布画线命令得出的树。递归图形函数的一个显著特点是看起来比较自然,我们可以看到分支的末梢非常精细。
《JavaScript高效图形编程(修订版)》——6.8 使用画布创建动画 基本上所有的位图动画系统都在幕后做类似上述循环的操作。在某些情况下第2步是可选的。例如,如果背景完全被实色、渐变或位图图像填补,那就没有必要清除它。第5步是必要的,这使用户有机会看到动画,并让浏览器有时间去做其他事情,否则该浏览器将立即被冻结。
《JavaScript高效图形编程(修订版)》——6.6 画布导出器 Adobe的Flash CS5+有一个画布导出器可以将部分Flash导出为JavaScript 画布源代码。这对想兼顾Flash和画布的开发者很有用。然而,由于该解决方案需要购买Flash创作工具,它对仅仅想开发画布的人来说可能不太划算。
《JavaScript高效图形编程(修订版)》——6.5 画布与Adobe Flash的对比 大多数网民都熟悉Adobe Flash。大量的在线广告内容、视频和游戏都使用了Flash。事实上,有很多网站是完全用Flash创建的。这是一个可以追溯到1996年的成熟插件,现在几乎所有系统上都安装Flash。
《JavaScript高效图形编程(修订版)》——6.4 画布与SVG的对比 有些人最初对苹果创建另一个浏览器图形标准持保留意见,他们可能认为SVG已经足够了。从表面上看SVG和画布提供类似的图形能力,但它们有一个根本的区别:SVG是一个高层的、基于XML的标记语言,可以通过创建XML元素属性来定义图像;而画布则提供了可以直接从JavaScript访问的绘图API。
《JavaScript高效图形编程(修订版)》——6.3 画布限制 同样,你不能给画布中绘制的元素添加事件(如鼠标点击),因为它们并不是有形的实体,而只是瞬态的绘图操作。你必须通过编程来实现这样的功能。
异步社区 异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。