Javascript的新领域——动态图片处理之SVG
Posted starrow
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Javascript的新领域——动态图片处理之SVG相关的知识,希望对你有一定的参考价值。
近三年前的一篇旧文,虽然在浏览器和相关技术标准方面有过时之处,但总体上对概念的理解、开发的思路和代码样例仍然有参考价值。
背景
当javascript被Netscape公司发明出来时,它被用来做一些琐细的事情,比如校验表单、计算日期、提示用户;随着Web的迅速发展,这种轻巧而灵活的语言被委以越来越多的任务,动态地修改页面内容,一致地处理事件,甚至无刷新地和服务器交互。然而,与传统的客户端编程相比,Javascript操作的对象限制在DOM模型之内,无法进行图形编程。所以长久以来,我们在设计网页时都仅仅是在“搭积木”,而且这些积木只有一种形状——长方形。这些长方形的积木就是应用在html元素上的“盒子”模型(box model)。每个盒子有边框 (border),边缘(margin)和填充(padding)。我们只能控制这些盒子的大小和有限的样式。这些方块的集合对于构建一个传统的文档页面已经足够了。但是Web的流行已经使网页承担的任务远远超出了传递文字信息。哪里有流行,哪里就有需求,哪里也就有创新。网页的美工设计已经使静态页面的美观程度丝毫不逊色于传统的客户端程序的界面。而创造更加互动的用户界面更是使在页面上创建和修改图片的可能十分吸引人。于是,两种技术应运而生,使得Javascript的功能扩展到图形领域。
数字化图片的两种方案
在介绍这两种技术之前,我们先来看看图片的数字化。将图片存储为数据有两种方案。其一为位图,也被称为光栅图。即是以自然的光学的眼光将图片看成在平面上密集排布的点的集合。每个点发出的光有独立的频率和强度,反映在视觉上,就是颜色和亮度。这些信息有不同的编码方案,在互联网上最常见的就是RGB。根据需要,编码后的信息可以有不同的位(bit)数——位深。位数越高,颜色越清晰,对比度越高;占用的空间也越大。另一项决定位图的精细度的是其中点的数量。一个位图文件就是所有构成其的点的数据的集合,它的大小自然就等于点数乘以位深。位图格式是一个庞大的家族,包括常见的JPEG/JPG, GIF, TIFF, PNG, BMP。
第二种方案为矢量图。它用抽象的视角看待图形,记录其中展示的模式而不是各个点的原始数据。它将图片看成各个“对象”的组合,用曲线记录对象的轮廓,用某种颜色的模式描述对象内部的图案(如用梯度描述渐变色)。比如一张留影,被看成各个人物和背景中各种景物的组合。这种更高级的视角,正是人类看世界时在意识里的反映。矢量图格式有CGM, SVG, AI (Adobe Illustrator), CDR (CorelDRAW), PDF, SWF, VML等等。
矢量图中简单的几何图形,只需要几个特征数值,就可以确定。比如三角形,只需要确定三个顶点的坐标。圆只需要确定圆心的坐标和半径。描述它的函数已知的曲线也只需要几个参数就能够确定。如正弦曲线、各种螺线等等。如果用位图记录这些几何图案,则需要包含组成线条的各个像素的数据。除了大大节省空间,矢量图还具有完美的伸缩性。因为记录的是图形的特征,图形的尺寸任意变化时,都只是做着相似变换,不会出现模糊和失真。相反位图的图片放大到超出原有大小时,各个像素点之间出现空缺,即使用某种算法填充,也会出现模糊锯齿等现象,不如矢量图精确。因而矢量图很适合用于记录诸如符号、图标等简单的图形。而位图则适合于没有明显规律的、颜色丰富细腻的图片。
两种技术
现在我们回到Web上的画图上。对应于图片数字化的两种方案,各有一种技术。我们按照它们产生的时间顺序来说。这篇文章中,笔者会介绍第一种——SVG。
SVG
第一种技术来自XML家族,叫做SVG(Scalabe Vetor Graphics)可缩放矢量图。作为一种通用的数据格式,XML自诞生之日起,就不断表现出表达一切可表达之物的抱负,不仅要接纳新出现的各种信息,还要接收历史上以其他各种形式存储的数据。其扩张版图的雄心,不亚于任何一位野心勃勃的君主。
XML适合于描述结构化的数据,所以你可能猜到了,如它的名字所示,SVG选择的视角是矢量图。实际上,SVG远不是第一种用XML描述图片的格式,甚至也不是第一种在Web上提出的XML与矢量图的组合的标准。在它之前的1998年,Macromedia和Microsoft向W3C提交了VML(Vector Markup Language),Adobe和Sun提交了PGML(Precision Graphics Markup Language),这两种都是基于XML的矢量图规范。随后,不希望互联网上的矢量图片标准被这些巨头垄断的其他公司在W3C内成立了一个专门小组SVG Working Group,在借鉴了前两种提案后,提出了SVG规范,随后被接纳为相当于标准的W3C推荐(W3CRecommendation)。以下是迄今为止SVG的主要发展历程:
2001-9 SVG 1.0成为W3C推荐。
2003-1 SVG 1.1成为W3C推荐。并演化出SVGTiny,SVG Basic和SVG Full不同级别的细则。
SVG1.2在之后的几年中一直处于工作草稿(W3C Working Draft)的状态,现已确定会被SVG 2.0取代。
SVG2.0将会完全重写SVG 1.2,以加入更多诸如CSS,HTML5的新特性。
第一个简单的例子
下面是一个很简单的矢量图的定义。SVG中各种元素和属性的详细说明可以在专门的参考中找到。本文中会在例子中对一些重要的元素和属性做说明。
清单1. 一个SVG文件
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" >
<circle cx="100" cy="100" r="40" fill="red"/>
</svg>
第一行的XML指令定义版本,并说明此文件引用到其他文件。第二行是文档类型定义,规定此XML中哪些是有效的SVG元素。这里引用的http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd正是第一行中standalone属性为no的原因。第三行开始是SVG的真正定义。circle元素指定画一个圆。cx、cy和r属性分别指定圆心的横坐标、纵坐标和半径。fill属性指定用红色填充此圆内部的区域。
画图比看图容易
将这段“文本”粘贴进任何一个文本编辑器,然后将文件保存为一个SVG文件,如sun.svg。你就已经画完了一幅图——一个红红的太阳。但是想要看它却不那么容易。需要用一些专业的绘图软件,比如Adobe Illustrator,CorelDRAW和GIMP才能显示这个图片。你的电脑上已经有的Windows画图、ACDSee都不支持这种格式。这是可以理解的,因为SVG是作为互联网上图片的一种标准。所以接下来看看怎样在浏览器中显示它——不幸的是,这仍然不像打开一幅JPG或者GIF那么简单。
各种浏览器对SVG的支持不一。总的说来,现在仍旧占据最大市场份额的IE不支持,其他主流浏览器,包括现在市场份额第二的Firefox以及Chrome、Safari和Opera都对SVG标准有不同程度的支持。IE6、7、8对SVG都没有原生的支持,需要专门的插件(如Adobe SVG Viewer)才能显示。目前还处于技术预览版的IE9将会支持。考虑到IE曾经占据的垄断性地位和微软有自身的竞争性的VML技术,这种“落后”并不奇怪。
不过这种情况,在发展迅速的浏览器市场瞬息万变。所以最好试试看您使用的浏览器支持下列哪种显示方法。
1. 使用<img>标签。
<img src=’sun.svg’>
将SVG与传统的互联网图片格式同等使用(现在只有Chrome、Safari和Opera支持)。
2. 使用<embed>标签。
<embed src="sun.svg" width="300" height="100"
type="image/svg+xml"
pluginspage="http://www.adobe.com/svg/viewer/install/" />
pluginspage属性的值是Adobe公司为不原生支持SVG的浏览器开发的插件Adobe SVG Viewer的安装地址。2009年1月1日Adobe已经终止对该产品的支持。
3. 使用<object>标签。
<object data="sun.svg" width="300" height="100"
type="image/svg+xml"
codebase="http://www.adobe.com/svg/viewer/install/" />
4. 使用<iframe>标签。
<iframe src="sun.svg" width="300" height="100" border="0" style="border-width:0">
</iframe>
下面是一个测试浏览器对html中各种使用SVG的方式是否支持的页面代码。sun.svg文件与该页面保存于同一目录。
清单2. 测试浏览器对SVG的支持
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE> SVG in HTML </TITLE>
</HEAD>
<BODY>
1. 使用<img>标签
<br>
<img src="sun.svg" width="300" height="100">
<br>
2. 使用<embed>标签
<br>
<embed src="sun.svg" width="300" height="100"
type="image/svg+xml"
pluginspage="http://www.adobe.com/svg/viewer/install/" />
<br>
3. 使用<object>标签
<br>
<object data="sun.svg" width="300" height="100"
type="image/svg+xml"
codebase="http://www.adobe.com/svg/viewer/install/" />
<br>
4. 使用<iframe>标签
<br>
<iframe src="sun.svg" width="300" height="100" border="0" style="border-width:0">
</iframe>
</BODY>
</HTML>
动态功能
如果仅仅是将SVG作为图片引用,则只发挥了它的静态功能。我们更感兴趣的是应用它的动态功能。SVG的动态功能包括两个方面。一为动画,二为支持脚本编程。
动画
SVG在设计时就加入了对动画的支持。这是通过另一种W3C颁布的动画语言SIML(Synchronized Multimedia Integration Language)实现的。SIML被应用时,与SVG结合得非常紧密。它与SVG一样,是一种声明性(declarative)的标记语言,通过元素(element)和属性(attribute)来定义动画的行为。这里只给出一个简单的例子,不做详细介绍。因为浏览器对它的支持还很有限;另外它声明性的本质也使表现力受到限制,不如使用脚本自定义动画灵活。
清单3. 用SIML实现的动画
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<polygon points="50,100 100,100 75,50" stroke="#660000" fill="#cc3333">
<animateTransform
attributeName="transform"
begin="0s"
dur="10s"
type="rotate"
from="0 0 0"
to="360 60 60"
repeatCount="indefinite"
/>
</polygon>
</svg>
polygon元素指定画一个多变形,这里给定了三个顶点,所以是一个三角形。
将上面的代码保存成一个SVG文件,在一个页面中引用,如果您的浏览器支持SIML,屏幕上会显示一个不断旋转的红色三角形;如果您的浏览器只支持SVG,将看到一个静止的红色三角形。
脚本可编程性
SVG是一个XML文件,用于XML编程的两种模型DOM和SAX也适用于它。因为SVG是被设计用于互联网,所以通过Javascript和DOM访问它就是最重要的应用模式。我们已经熟悉通过Javascript和DOM动态地修改HTML,同样我们也可以在浏览器中动态地创建、修改和删除图片,这也将是本文之后在SVG方面的重点。
为了演示这些动态功能,我们采取和上面不同的在页面中使用SVG的方式——在XHTML(XML的XML版本)直接写入SVG的源文本,而上面的四种方式SVG的定义都保存在和页面不同的另一个文件中。这样做有两个原因。一是在支持XHTML和SVG在浏览器中,可以通过Javascript直接访问和修改SVG。二是在互联网的未来标准HTML 5中,SVG就可以这样直接在HTML中定义,就像其他HTML元素一样。
之后的几个例子都可以在Firefox中运行,但无法使用IE。因为要到版本9,IE才会加入对XHTML的支持(目前的IE只支持将XHTML作为HTML解释),再次显示了拥抱公开标准的迟缓。
我们的第一个例子是一个进度条。在Firefox中载入下面的XHTML页面,会显示一个绿色的运动的进度条。
图1. 进度条示例
清单4. 进度条代码
进度条
<script language='JavaScript'>
/* = 100)
done = 100;
bar.setAttribute('width', length);
else
done = value;
bar.setAttribute('width', Math.round(done * length / 100));
return stem;
//进度变化某个值。
function advance(step)
return to(done + step);
//以下给进度条对象添加方法。
//获得当前进度值。
stem.getDone = function()
return done
;
stem.reset = reset;
stem.to = to;
stem.advance = advance;
return stem;//返回可供脚本使用的进度条对象。
//测试进度条对象。
function testBar()
var bar = ProgressBar();
//此内部函数每运行一次,增加进度值1,直到进度值为100。
function test()
if (bar.getDone() === 100)
clearInterval(id);
else
bar.advance(1);
//每十分之一秒改变一次进度。
var id = setInterval(test, 100);
//页面载入后开始测试。
window.addEventListener('load', testBar, true);
/* ]]> */
</script>
对这个XHTML需要做一些说明。
<htmlxmlns="http://www.w3.org/1999/xhtml">
XHTML的根元素为html元素,xmlns属性指定XHTML的命名空间。
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100" style="border:1pxsolid; width:100px; height:100px; ">
在XHTML中直接插入svg元素,并指定命名空间等其他属性。
viewBox定义矢量图可见的坐标空间,四个数字依次是原点的x坐标、y坐标、平面的宽度、高度。SVG的坐标空间符合计算机中指定屏幕空间的惯例,x坐标轴的正方向向右,y坐标轴的正方向向下。
style属性指定svg元素的各种外观特性。SVG与HTML一样,可以应用CSS定义外观,并且有一些专门的特性。
1. XHTML中的Javascript代码被包含在/* <![CDATA[ */ 和 /* ]]> */ 之间。
在HTML文件中不需要这样做。因为在HTML中<script>标签内的Javascript代码被解释为CDATA(Character Data,XML中的一种类型,用于包含任意的字符数据);而在XHTML中<script>标签内的部分被解释为PCDATA(Parsed Character Data,也是XML中的一种类型,为字符数据和元素的混合内容),所以也要通过XML的语法检查,而Javascript代码显然不符合XML的标签的定义语法。解决方法就是在代码外人工加上![CDATA[和 ]]>标注,使得XML的语法校验器忽略这段内容。但是这样会带来第二个问题,有些浏览器不认识CDATA标注,因而这些代码又无法通过Javascript的语法检查。所以我们在CDATA标注两侧再加上Javascript的注释标记。这样<script>标签内的代码既能通过XML的语法检查,又能被Javascript引擎认识。
2. <svg>标签内有一个<g>标签和两个<rect>标签。
g元素用于分组。分组不仅可以使SVG的内容结构清晰,同一组内的对象还可以被集体操作。rect元素代表一个矩形;x、y、width和height属性分别指定矩形左上顶点的横坐标、纵坐标和矩形的宽度、长度;stroke属性指定图形外框的线条颜色。我们用第一个空心的矩形显示进度条的外框,第二个实心的绿色矩形显示变化的进度。为了在脚本中方便地访问,我们设置了绿色矩形的id属性。
3. 代码说明
在Javascript脚本中我们用DOM先后获得绿色矩形对象并修改它的宽度属性。getElementById和setAttribute的用法和在HTML中没有两样。值得注意的是,有些我们在操作HTML时使用的方法,在XML中是不存在的,如根据名称获取元素的getElementsByName。
这个例子中前三点特别的设定有些麻烦,不过这些在正在获得越来越多支持并且很快将成为互联网的现实标准的HTML 5中都是不必要的。在HTML 5中不需要在html和svg元素中指定命名空间,svg和其中的各种标签会被自动识别。Javascript代码也会和在现在的HTML页面中一样,不需要在两侧加上CDATA标注。
事件
SVG中的元素同样支持用户界面的事件。因此我们可以通过鼠标、键盘触发的各种事件改变SVG中的图形。这就使得在整个页面上可以进行丰富的图形的互动,而不需要借助于Flash插件。下面通过几个例子来说明对事件的运用,使用的都是DOM3事件规范中定义的方法。
模拟控件
HTML中的单选钮、复选框、下拉列表等标准控件为用户输入和显示数据提供了各种友好的方式。过去我们只能“使用”它们,现在我们可以模拟甚至创造新的控件。我们先来模拟一个简单的单选钮的图形和行为。
图2. 模拟单选钮
清单5. 模拟的单选钮1之代码
模拟单选钮1
<script language='JavaScript'>
/* */
</script>
代码说明:
1. SVG
这里共有三个circle元素,其中两个作为空的选项的圆圈,第三用于标记选中的状态。circle中控制大小和位置的几项属性我们之前都看过了。stroke-width属性用于控制图形外框线条的宽度。name属性是我们自定义的,用于将一组单选钮关联在一起。
在SVG中嵌入文字需要使用text元素。x和y属性用于控制文字的位置,x为左端的坐标,y为下端的坐标。
当然,以上只是模拟了单选钮响应鼠标点击的外观,真正的控件还必须包括读和写数据。为此,我们将扩展上面的例子,提供一个可以单独使用的“单选钮”。
图3. 更完善的模拟的单选钮
以下为实现这样一个单选钮的代码。代码中有详细的注释。
模拟单选钮2
<script language='JavaScript'>
/* -1 && index < items.length)
setIndex(index);
//读取选中项的index,相当于在HTML中读取某个单选钮的checked属性。
stem.getCheckedIndex = function()
return checkedIndex;
//读取选中项的值。
stem.getValue = function()
return value;
init();
return stem;
//测试模拟的单选钮。
function testRadio()
//获取用脚本和SVG模拟的单选钮。
var rad = RadioButton('radColor');
//测试选中第二项。
rad.setCheckedIndex(1);
//使用一个按钮,显示单选钮选中项的序号和值。
document.getElementById('testRadio').addEventListener('click', function()
var msg = 'index: ' + rad.getCheckedIndex() + ' value: ' + rad.getValue();
document.getElementById('message').innerHTML = msg;
, true)
window.addEventListener('load', testRadio, true);
/* ]]> */
</script>
模拟单选钮
最后,为了展示SVG能创造的生动图形应用的可能性,这里给出一个更加复杂和有趣的例子。一个圆、两个点和一段弧线构成的表情是互联网上著名的符号。这个简单的图形不仅可以轻易被看成一张脸,而且通过改变弧线的形状,还可以表现从笑脸到哭脸的不同表情。它显示了人脑识别人脸的复杂行为是以模块和模糊的方式进行的,这种抽象的处理方式也为展现SVG的功能提供了一个良好的场合。我们先画出这样一张高度简化的脸,再创建一个滑动条控件,最后用拖动滑动条的方式来控制脸中弧线的弯曲程度和方向。如果把滑块的高度解释成象征一个人的压力,那么我们就看到了随着压力的改变,人的表情是如何变化的。
图4. 一个更复杂的例子
此代码如下:
Graph Demo
<script language='JavaScript'>
/* = barBox.y && sliderY<= barBox.y + barBox.height - sliderBox.height)
slider.setAttribute('y', sliderY);
//d='M-18,18 Q0,-2 18,18'
//滑动条高度为100,中点纵坐标为-4,笑脸控制点纵坐标变化范围为-2到38。
var y=Math.round(((sliderY-(-4))/50)*20+18);
mouth.setAttribute('d', 'M-18,18 Q0,'+y+' 18,18');
, false);
//DOM Level 3还没有列出与拖放相关的事件,但是Firefox(其他浏览器如IE也类似)已经实现了。
//如果不取消拖放,则当鼠标拖动滑块时,有时会出现拖放无效的提示,并导致鼠标键释放的事件无法被监听到。
svg.addEventListener('dragstart',
function(event)
event.preventDefault();
event.stopPropagation();
, false)
init();
addListeners();
//检查一个点是否在某个Bouding Box以内。
function isInBox(point, box)
return point.x >= box.x &&
point.x <= box.x + box.width &&
point.y >= box.y &&
point.y <= box.y + box.height;
//测试
function test()
var s = Slider('pressure');
window.addEventListener('load', test, true);
/* ]]> */
</script>
总结
本文介绍了一种用于互联网的基于XML的矢量图技术SVG的基本概念,并通过一些实例显示了当与Javascript结合时,这项技术的灵活性和广泛的应用前景。
参考资源
l SVG,维基百科上对SVG的介绍。
l SVG Tutorial - SVG Examples,一个不错的SVG教学网站。
l Pike's SVG Tutorial,另一个不错的SVG教学网站。
l SVGScripting Reference,MSDN上非常详细的SVG参考。
以上是关于Javascript的新领域——动态图片处理之SVG的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 javascript/node js 从数据库中将图片动态加载到网页上