在传统的网页应用中,报表是很重要的一块呈现内容,而其中的图表控件更能够直观的反应各种数据。随着混合模式手机应用的推广,相应的图表控件也需要在手机上进行呈现,这就引起了一些在手机环境下才会产生的问题。
所以开发者如果可能掌握一种自由度更高的绘图方法,在有特殊的情况下使用它,就可以让图表在手机端更好的展示了。HTML5 中的 CANVAS 就是一个很好的应用编程接口,通过它可以在手机端完成各种复杂的需求。
回页首
CANVAS 最早是应用在苹果系统中,用于绘制仪表盘等控件的。在 HTML5 标准中,它不但可以快速的绘制图像,还可以提供各种便利的接口,提高开发者的编码效率。它其实是在浏览器中开辟了一块独立的区域用于开发者绘制图片,在这块区域里面开发者可以通过系统提供的 API 进行诸如直线、圆弧、曲线等基本图形的绘制;也可以通过接口显示文字和图形;更高级的是可以通过操作区域中的每个像素点,设定图形的颜色和透明度等信息。从现有的情况来看,CANVAS 的功能是很强大的,几乎可以实现各种高级的绘图功能。
虽然现在流行的浏览器都支持 CANVAS 这个功能,但是为了避免错误,在使用 CANVAS 之前可以判断浏览器是否支持这个控件,只需要简单的检测可否获得画布的上下文即可。
虽然在 CANVAS 绘图时,可以动态指定各种颜色和大小,但是 CANVAS 作为基本的元素还是可以指定它的边框、边距、缩进等等属性;同时,CANVAS 中一些元素的属性会默认继承 CANVAS 的 CSS 属性,比如文字的默认属性就是从 CANVAS 中继承来的。在此基础上,CSS 设定各个元素的方法在 CANVAS 中也同样适用,所以熟悉 CSS 对于在 CANVAS 中设定各个值会有很大的帮助。
在 CANVAS 中描绘基本图形就像在一个指定的坐标轴内画图一样,不过这个坐标轴的方向和正常的坐标轴有所不同,图 1 是一个 CANVAS 的坐标轴系统,它的 Y 轴朝下 X 轴朝右,而普通的坐标系统 Y 轴方向是向上的。
图 1. CANVAS 坐标轴
在几何中,研究者可以通过平移坐标轴来简化问题,那么在使用 CANVAS 绘制图表时,开发者也可以通过类似的方式来重用一些函数。比如在应用中可能需要绘制多个柱状图,利用平移坐标轴,就可以把柱状图的左下角统一作为坐标系的零点进行考虑,这样图形只需要考虑柱状图的长宽,而不需要涉及在整张图中与零点的位置了。清单 1 中的三行代码说明了三个步骤:首先记录绘图的上下文,然后把坐标轴平移到一个位置(此时 chartX 和 chartY 就成为了新坐标轴的零点),绘制完成图像后再重置上下文,便又回到了原始系统。这个代码会在下面的实例中体现出很大的作用。
清单 1. 平移代码
context.save(); context.translate(chartX, chartY); // draw code context.restore(); 使用渐变和图片
在图表中有时候会用到一些已有的图片,比如用户的头像、商品的等级,这时候就需要利用 CANVAS 读入这些图片,然后把它们展现在区域中,这会让图表显得更加生动。至于渐变在目前的应用中用到较少,主要原因是没有好的应用场景。
一般的图表都会用到文字,用于直观的描述一些信息。普通的文字呈现是简单的,类似在 CSS 中定义文字的大小、字体、颜色等就可以描绘出来;在高级应用中,需要让文字进行旋转,从而更好的符合整体图形的呈现。
CANVAS 和普通的元素一样也可以进行事件监听,比如点击、移动。通过进行监听不同的事件,可以进行一些高级的处理。比如监听按下、移动和释放事件,就能模拟用户拖拽的事件了。在 CANVAS 中监听事件需要注意的是用户点击时在 CANVAS 中的坐标和在整个浏览器中坐标的转换,在接下来的实例中会详细介绍。
列举了那么多 CANVAS 提供的基础功能后,本文的余下部分将通过一个实例来具体展现各个功能的在代码中的调用情况,便于读者在开发过程中利用 CANVAS 绘制高级图像。
回页首
为了更好的展现 CANVAS 的不同功能,实例中的图像较为复杂,在实际的图表中只需要涉及其中的一部分。整个例子有五部分功能,分别是:图表初始化、用户信息描绘、球队描绘、能力描绘和信息查看。
手机的长宽区别较大,为了更好的利用手机提供的屏幕大小,应用在初始化的时候需要确认 CANVAS 的高度和宽度。清单 2 描述了完成这个功能的代码。
清单 2. 初始化 CANVAS
this._width = jQuery(window).width(); this._height = jQuery(window).height(); this._canvas = document.createElement("canvas"); jQuery("body").append(this._canvas); this._canvas.width = this._width; this._canvas.height = this._height; this._context = this._canvas.getContext("2d");;
这段代码中利用了 jQuery 框架中的一些函数,这样可以方便的获得当前窗口的宽度和高度。但是演示时会发现,在页面上会有两个滚动条,这可能是浏览器在解析 CANVAS 对象时存在的一个问题,将 CANVAS 的 display 属性设置为 block 就可以解决这个问题。当窗口初始化或者调整大小后调用这个函数,就动态的实现 CANVAS 的长宽变化,从而和窗口大小相吻合。
在这部分功能中,主要实现下面的三个关键点:坐标轴的平移和旋转、图片的描绘和文字的描绘。
在电脑上浏览网页时,浏览器的长度一般是大于宽度的,所以图表是横向呈现。但是在手机上长度小于宽度,如果继续横向呈现,效果就比较差。所以实例中利用了 CANVAS 提供的旋转功能将图表横向呈现,图 2 中表述了实际屏幕和虚拟屏幕的关系。
图 2. 坐标轴平移和旋转
虚拟屏幕是代码中实际画出的图形,实际屏幕是最终呈现的图形,从图 2 可以看到,虚拟屏幕在向右移动了一个屏幕宽度后,再旋转九十度就是实际屏幕的位置,所以相应的代码为清单 3 所示。
清单 3. 初始化 CANVAS
this._context.save(); this._context.translate(width, 0); this._context.rotate(Math.PI / 2); this._context.restore();
可以看到清单 3 和清单 1 的代码很相似,所以为了更好的使用 CANVAS 的接口,开发者需要掌握一定的数学知识,只有这样才能完成一些必须的工作,后面的代码还将涉及。
图片的描绘首先需要预读入这张图片,所以在代码中,应用先初始化了一个 Image 对象,然后绑定 load 事件,再读入相应的图片。一旦成功,这个对象就存放了图片的信息,可以应用 CANVAS 函数描绘出来。这里有两点需要说明,第一是 load 事件,因为图片读入是一个异步事件,所以不能按照顺序结构来,需要利用回调函数;第二是图片的缩放,在本例中,应用根据图片的宽度和实际的展现宽度进行比较,确认图片需要缩放的比例。
文字的展示主要通过 CANVAS 中的 context 对象进行设定,包括文字的字体、文字的颜色和文字的对其方向。文字的起始点是和字的底边对其的,所以在显示文字时需要留有一定的空白,否则文字会看不到。而对其方式是以 Y 轴为标准的:当居中时文字分布在 Y 轴的两边,向左时文字在 Y 轴的右边。这些都是描绘文字时需要注意的地方。
还需要注意一点的是文字的颜色和填充图像的颜色是一个属性,所以当文字和填充图像颜色不同时,要分别设定这个值。
在这部分功能中,主要实现下面的两个关键点:绘制矩形和绘制圆弧。
绘制矩形是一个很基础的工作,但是却有几个需要注意的地方:首先在画一个独立的矩形时,最好加上 beginPath 这句话,因为只有这样,之后的路径才是一条新路径;新路径可以设定新的线条粗细、颜色和样式等等属性;然后是利用 closePath 封闭路径,这样可以显示的告诉维护者这里的代码描绘的是一个封闭的图形(不光是矩形);最后就是线条的粗细设定,如果线条的宽度是 1,那么起始点的坐标最好设定成 a+0.5(a 为整数),因为这样 CANVAS 画的线条才真正会呈现 1 的宽度。
在描绘圆弧时,需要进行一定的计算。整个圆只需要弧度从 0 到 2π就可以了,但是如果需要画的是一段圆弧就需要进行计算了。比如在实例中,圆弧的半径和圆弧两点间的距离已知,这时候就可以利用三角函数知道需要绘制弧度的起始点了。
在绘制球队时,为了描述球队主力队员的位置,需要画一个球场。在这里,代码利用了各种基本的函数,包括移动、画线、设置线条属性等等。所以利用 CANVAS 虽然功能强大,但是有时候会需要很多重复的工作。
在_drawOneMember 函数中,主要涉及了对于平移坐标轴的应用,同时也演示了文字和图形的合作:首先每个对象的基本绘制函数是相同的,只不过显示在坐标轴的起始方位不同,这个时候就可以利用平移坐标轴来统一这个过程;在具体的一个对象中,需要描绘三个元素,对象的名称、位置和综合能力,这就牵涉到如何让图形和文字更好的协作。现在 CANVAS 提供函数去获得文字的宽度,这样就可以帮助开发者对文字进行左、中或者右的对其,但是对于取得文字的高度还是一个比较复杂的过程,所以一般利用经验值去协调彼此的坐标高度。从代码中可以看到文字的颜色和填充图像的颜色是如何被分开设置的。
在这部分功能中,就要描述一个功能:如何画一个正五边形,然后在上面显示相应的数据。
在一个给定的矩形框内(假设高度大于宽度)画一个正五边形需要进行详细的运算,图 3 中给了一个相应的几何模型。
图 3. 正五边形
从图中可以看到,为了方便计算,加入了两条红线作为辅助线。当宽度确定时,X0 的坐标是最容易获得的,为 (w/2, 0)。X1 的坐标就复杂一点,它的 Y 轴高度和 X 轴长度成一个比例,比例的值为 tan54°(这里需要一定的三角函数知识,请读者参阅相关书籍),所以 X1 的坐标为 (w, w * tan45°/2)。X2 的坐标是最复杂的,它的 X 轴坐标为 w / 2 加上正五边形边长的二分之一,那么如何求得正五边形的边长呢?刚才在求 X1 的坐标时,我们已经知道了 X1 到 X0 在 X 轴的距离,而 Y 轴距离通过计算也获得了,所以利用勾股定理就能获得正五边形的边长,它的计算结果为 w /(2 * cos36°)。X2 的 Y 轴坐标利用三角函数也可以获得,就是 w / (4 * (cos36° * tan18°))。X3 和 X4 分别和 X2、X1 对称,就不再详细描述计算结果,最后的坐标如清单 4 所示。
清单 4. 正五边形坐标
x0 = w / 2, y0 = 0; x1 = w, y1 = w * tan36 / 2; x2 = w / 2 + w / 4 / cos36, y2 = w / 4 / (cos36 * tan18); x3 = w / 2 - w / 4 / cos36, y3 = w / 4 / (cos36 * tan18); x4 = 0, y4 = w * tan36 / 2;
为了描绘实际的值(图中的褐色线条),需要通过获得“原点”O 的坐标,然后通过一次函数的公式,计算获得各个点的实际坐标,详细的坐标如清单 4 所示。
清单 5. 能力点坐标
ox = w / 2, oy = w / 2 * (1 / tan54 + 1 / tan72); px0 = (x0 - ox) * p0 + ox, py0 = (y0 - oy) * p0 + oy; px1 = (x1 - ox) * p1 + ox, py1 = (y1 - oy) * p1 + oy; px2 = (x2 - ox) * p2 + ox, py2 = (y2 - oy) * p2 + oy; px3 = (x3 - ox) * p3 + ox, py3 = (y3 - oy) * p3 + oy; px4 = (x4 - ox) * p4 + ox, py4 = (y4 - oy) * p4 + oy;
从描绘正五边形的过程中可以看到,利用 CANVAS 的接口绘图,虽然带来了很多的便利,但是同时也需要很多基础的数学知识(三角函数、一次函数)去完成数学建模,达到预期的绘图效果。最后文字的描述和球队描绘类似,不再赘述。
CANVAS 也和其他的 DOM 元素一样可以监听各种事件,在 CANVAS 中用户点击的具体位置会产生两个不同的坐标,分别是在 CANVAS 中的坐标 (CX, CY) 和在整个 DOM 页面中的坐标 (DX, DY)。一般来说,前者主要是为了在 CANVAS 中继续绘图,比如将一个矩形移动到新的位置;后者则是为了显示提示信息,比如在 DOM 节点中有一个显示详细信息的 DIV,那么通过这个坐标值,就可以将 DIV 设定为 absolute,显示在合适的位置上。具体的公式如清单 6 所示:
清单 6. 事件坐标公式
CX = e.x; CY = e.y; DX = CX - e.target.offsetLeft + document.body.scrollTop; DY = CY - e.target.offsetTop + document.body.scrollLeft;
其中 e 是相应的触摸事件对象。可以看到影响整个 DOM 坐标的除了本身点击的位置,还有 CANVAS 相对于 DOM 的偏移量,以及整体 DOM 卷轴的位置。
回页首
在对实例进行了详细分析后,CANVAS 强大的绘图能力一定已经被读者所了解。在熟悉它提供的不同 API 基础上,开发者还需要对相应的数学知识有足够的了解才可以更好的绘制图像,创建自身应用需要的内容。
当然强大的 CANVAS 不但能实现绘图功能,而且为将来的网页开发带来了更多的可能,以下列出了七种:游戏,毫无疑问游戏在 HTML5 领域具有举足轻重的地位。HTML5 在基于 Web 的图像显示方面比 Flash 更加立体、更加精巧;广告,Flash 曾经辉煌的时代,智能手机还未曾出现。现在以及未来的智能机时代,HTML5 技术能够在广告上发挥巨大作用,用 CANVAS 实现动态的广告效果再合适不过;模拟器,无论从视觉效果还是核心功能方面来说,模拟器产品可以完全由 JavaScript 来实现;远程计算机控制,CANVAS 可以让开发者更好地实现基于 Web 的数据传输,构建一个完美的可视化控制界面;字体设计,对于字体的自定义渲染将完全可以基于 Web,使用 HTML5 技术进行实现;图形编辑器,图形编辑器将能够 100%基于 Web 实现;其他可嵌入网站的内容,类似图表、音频、视频,还有许多元素能够更好地与 Web 融合,并且不需要任何插件。这些内容都是将来的手机应用开发可以尝试的领域,通过认真的学习可以寻找到 CANVAS 更多更合理的应用方式和技巧。