《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

相关文章
|
3月前
|
JavaScript 数据可视化
基于fabric.js的图片编辑器, 画布背景实现原理
基于vue3 + fabric.js + vite + element-plus + typescript等技术,画布背景原理分析
基于fabric.js的图片编辑器, 画布背景实现原理
|
JavaScript
JS基础之解构赋值
解构赋值 在js中,我们经常会将对象或者数组里面的一部分数据作为参数传递给函数,如果我们使用传统的.方法会很麻烦。
|
JavaScript 前端开发
JavaScript 入门基础 - 运算符(三)
文章目录 JavaScript 入门基础 - 运算符(三) 1.什么是运算符 2.表达式和返回值 3.算术运算符概述 4. 赋值运算符 5.递增和递减运算符 5.1 递增和递减运算符概述 5.2 递增运算符 5.2.1 前置递增运算符 5.2.2 后置递增运算符 5.2.3 后置和前置运算符的区别 6. 比较运算符 7. 逻辑运算符 7.1 逻辑运算符概述 7.2 逻辑与 7.3 逻辑或 7.4
121 0
JavaScript 入门基础 - 运算符(三)
|
存储 JSON JavaScript
JavaScript 入门基础 - 变量 / 数据类型(二)
JavaScript 入门基础 - 变量 / 数据类型(二)
87 0
JavaScript 入门基础 - 变量 / 数据类型(二)
|
JavaScript 前端开发 物联网
JavaScript 入门基础 / 概念介绍(一)
JavaScript 入门基础 / 概念介绍(一)
112 0
JavaScript 入门基础 / 概念介绍(一)
|
JavaScript 前端开发 Java
JavaScript的基础使用
JavaScript的基础 一、javascript简介 JavaScript简称js,最初由网景(现在的Mozilla)公司创建,由于商标冲突原因,其标准版本命名为ECMAScript,但是一般人们还是叫JavaScript,只在谈标准的时候说到ECMAScript这个名字。值得注意的是JavaScript与java没有任何关系,就像雷峰塔(神话中镇压白娘子的塔)和雷锋。此外js(JavaScript)和jsp(java servlet pages)也没有关系。   js的工作分为两部分,一部分属于js语言本身的特性,而另一部需要依靠宿主环境(web浏览器)才能完成。 二、javascri
JavaScript的基础使用
|
JavaScript 前端开发
js 模块化基础和模块规范AMD、CMD、ES6模块
js 模块化基础和模块规范AMD、CMD、ES6模块
83 0
|
JavaScript 前端开发
原生js制作选项卡详解,适合无基础的人学习
原生js制作选项卡详解,适合无基础的人学习
109 0
原生js制作选项卡详解,适合无基础的人学习
|
JavaScript 前端开发
vue js文字跑马灯基础版本
提供两种解决思路
253 1
|
存储 JavaScript 前端开发
JavaScript基础(一篇入门)
JavaScript基础(一篇入门)
183 0
JavaScript基础(一篇入门)