开发者社区> 异步社区> 正文

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

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

本节书摘来自异步社区《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库)
  • GLGE
  • C3DL
  • 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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
数组旋转,来来来,走个K步~
在前端算法面试中,数组是经常被问到的、使用到的。今天我们来看一道经典的前端基础面试题:【数组旋转K步】。
5 0
如何实现一个队列
队列,是一种常见的逻辑数据结构。具备什么特点呢?经常性的我们会听到一个类比“队列就像队伍过桥洞”,队列中的元素遵循了“先进先出、后进后出”的原则。 在JavaScript中有很多的方式来实现一个队列,让我们一起来看看都是如何实现的呢?
4 0
17 网络编程
Java SE提供java.net包,其中包含了网络编程所需要的最基础一些类和接口。这些类和接口面向两个不同的层次:基于Socket的低层次网络编程和基于URL的高层次网络编程,所谓高低层次就是通信协议的高低层次,Socket采用TCP、UDP等协议,这些协议属于低层次的通信协议;URL采用HTTP和HTTPS这些属于高层次的通信协议。低层次网络编程,因为它面向底层,比较复杂,但是“低层次网络编程”并不等于它功能不强大。恰恰相反,正因为层次低,Socket编程与基于URL的高层次网络编程比较,能够提供更强大的功能和更灵活的控制,但是要更复杂一些。
1 0
富文本编辑器Quill 介绍及在Vue中的使用方法
在Web开发中,富文本编辑器是不可或缺的一个功能组件,掌握少量基础语法就能让一篇文章实现较为不错的排版效果,即见即所得
1 0
从0开始实现一个好用的Avatar头像组件-React版
在我们日常项目开发中,经常会遇到展示用户头像、空间头像、图片缩略图等相关需求。 展示头像的需求看起来似乎很简单,不就是展示一张图片吗?!如果你这样想,在你实现这个需求的时候,PM肯定会来来回回找你好几次。
3 0
经典面试题:有效的括号
在前端算法面试中,经常会被问到的一道题是 - “有效的括号”或者是称呼为“字符串是否是括号匹配”,什么意思呢?
3 0
ECS实验体验
ECS的简单使用体验报告
6 0
二叉树与前序遍历、中序遍历、后续遍历
二叉树相关的面试题也是前端面试中非常重要的一部分。那二叉树是什么样的一种结构呢?
1 0
一起来快排吧 | 数组排序
数组快速排序(快排)也算是前端面试的经典入门问题了,作为一个前端程序员掌握快排技能也是必须滴~
1 0
5. 使用Flask蓝图(blueprint)
使用Flask蓝图(blueprint)
1 0
+关注
异步社区
异步社区(www.epubit.com)是人民邮电出版社旗下IT专业图书旗舰社区,也是国内领先的IT专业图书社区,致力于优质学习内容的出版和分享,实现了纸书电子书的同步上架,于2015年8月上线运营。公众号【异步图书】,每日赠送异步新书。
12049
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载