SVG——新一代Web设计及互动媒体的革新

SVG与XML商业图表实务

来源:IBM developerWorks

  学习如何使用XML、PHP和可升级矢量图形(SVG)标准创建商业图像。SVG标准为图像提供了无限级的向量缩放、可视化效果以及基于脚本的交互性。

  我承认,我是一个图像痴迷者。从孩提时候起我就喜欢有漂亮图像的计算机。这也是与TRS-80相比我更喜欢Apple II计算机的原因。不过有谁不喜欢图片呢?谁不曾被Pixar电影征服?"一张图片胜过千言万语"这句老话没有错,因为一幅图片能够又快又容易地传达大量信息。

  图像对于商业数据的重要性其他任何地方都比不上。通过SVG之类的标准来充分利用图像代码非常重要,因为众所周知,Web上早就不缺乏图像了。当然可以把图片放在网页中,但通常这些图片的作用不大。这些照片不能缩放和滚动,不能交互,不能很好地打印和调整比例。不过,我相信Web 2.0将改变这种局面。不再需要强调这种技术的重要性。本文的目的是给用户以包括图像的体验。打开Google的Finance页面,如图1所示。查看股票的时候,可以使用交互式图像控件滚动数据,找到感兴趣的地方。是否使用Macromedia Flash实现有关系吗?没有。重要的是最终结果——客户体验。

图1 Google Finance页面

  本文将通过例子说明如何使用Adobe SVG格式和PHP编程语言创建漂亮的交互式图形。首先,了解一下SVG的背景知识及其与Web图像技术的关系。

1 可升级矢量图形

  Adobe的SVG标准是一种基于XML的表示向量图形的格式。基础是直线、矩形、形状、图片和文本这些元素。所有这些元素都在"视图坐标"中指定,坐标值不是像素,只是适合应用程序的需要而定义的任意数值范围。这样就可以将XML指定的图像模型呈现到任意的图像空间中 -- -- 无论多么大或者多么小 -- -- 并进行适当的缩放。向量图像可以用打印机的最大分辨率打印,不会出现位图放大打印时常见的锯齿。
  这种格式还允许对任何对象或对象组应用特效。其中包括投影、斜角、纹理、外测发光、内测发光等等。如果熟悉Adobe PhotoShop或Elements,就会知道这些效果。还可以使用旋转、倾斜、透明、剪裁等技术。
  不仅如此,SVG的标记还可用于动态改变这些属性,因此可以沿着路径移动图形对象或者实现淡入淡出效果。此外,SVG还允许在模型中添加JavaScript代码,为图形元素、效果和动画加上行为。
  我第一次看到SVG的时候,立刻被它吸引并钻了进去。照我看来,Adobe是把PhotoShop引擎变成了能够嵌入到网页中的控件。事实上,今天看来仍然如此。不足之处是,我发现并非所有的客户机上都安装了SVG,而且安装它需要下载某些软件。我不可能这样要求客户。因此有段时间我把SVG放在了一边,直到最近发现SVG的一个子集安装到了Mozilla Firefox 1.5中。现在,我想对于SVG来说事情将向好的方面转化。
  但是我们再后退一步,将SVG置于Web图像的大背景中。

2 SVG和它所属的世界

  几年以前,如果想为Web创建图像,选择很有限。您可以即时创建PHP图片和建立.jpeg、.gif或.png文件。但是这些图像往往很简陋,因为PHP图像库非常原始,不支持特效。此外图像的缩放也不够好。
  技术有了一点进步,现在有更多的选择了。当然,其中包括SVG。但是还有Flash。Flash可以使用画布对象和JavaScript代码绘制任何图像。还能够从服务器上直接以XML或JavaScript Serialized Object Notation (JSON)的形式把数据读入到图像中。
  在Flash之上有两种相对较新的技术。第一个是Adobe Flex,这种基于XML标记的语言可以呈现为Flash,并且包含图形库。Flex的竞争对手之一是Laszlo。Laszlo也使用标记,优点是开放源代码。
  还可以在浏览器中使用<canvas>标记。这种新标记是一个画布,可以在其上绘制直线和矩形、放置图片、进行旋转等。听起来似乎不错,但Microsoft Internet Explorer不支持它 -- -- 目前来说。幸运的是,Google有一个开放源代码项目InternetCanvas,在Internet Explorer中提供了相同的能力。
  刚出现的另一种选择是Microsoft Windows Presentation Foundation (WPF),它提供的XML标记可以创建能够嵌入浏览器的Windows User Interfaces。是不是只能用于Microsoft Windows呢?的确。但是还有WPF/E,它为Mac和Windows上的所有主流浏览器提供了WPF的一个功能子集。
  确实有不少选择。不过本文选择SVG,并结合使用PHP为股票数据绘制一些图像。

3 SVG的Hello World

  为了确保您的Firefox 1.5浏览器能够正确处理SVG或者有一个安装了SVG插件的浏览器,我提供了一个简单的类似"Hello World"的例子供您测试。首先,我创建了一个文件start.html,如代码1所示。作为引用.svg文件的网页。

  代码1 Start.html

<html>
<body>
<embed src="/start.svg" height="300" width="300" type="image/svg+xml"
pluginspage="http://www.adobe.com/svg/viewer/install/"
style="border: 1px solid black; padding:5px;"/>
</body>
</html>

  然后需要一个start.svg文件,如代码2所示。可以看到.html文件中<embed>标记引用了该.svg文件。

  代码2 Start.svg

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg style="shape-rendering:geometricPrecision;"
viewBox="0 0 100 100" xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid meet">
<path fill-rule="nonzero"
style="fill:#000000;stroke:#FF0000;"
d="M0 0 L100 100 Z"/>
<path fill-rule="nonzero"
style="fill:#000000;stroke:#00FF00;"
d="M50 0 L50 100 Z"/>
<path fill-rule="nonzero"
style="fill:#000000;stroke:#FF0000;"
d="M0 100 L100 0 Z"/>
<path fill-rule="nonzero"
style="fill:#000000;stroke:#00FF00;"
d="M0 50 L100 50 Z"/>
</svg>

  在浏览器中打开该文件时,可以看到图2所示的结果。

图2 svg测试文件

  回头再看看那个文件,其中定义了四条路径:两条从角到角,另外两条绿色的沿中心从上到下。如果看到该图像,说明您的浏览器至少能呈现SVG的一个小子集。

3 获取数据

  绘图从数据开始。这里我使用两家公司31天的股票数据。包含股票数据的XML文档的一部分如代码3所示。

  代码3 data.xml片段

<stocks>
<stock>
<day high="35.13" low="32" close="33.75" />
<day high="32.25" low="28.75" close="31.75" />
<day high="29" low="28.5" close="28.87" />
<day high="29.25" low="28.75" close="28.75" />
<day high="29.5" low="28.5" close="29.25" />
<day high="30.25" low="29" close="29.25" />
...
</stock>
</stocks>

  格式很简单。根标记<stocks>包含一组<stock>标记。每个<stock>标记包含一些day标记,带有high、low和close属性。

  接下来要编写将该文件读入内存以便用于生成SVG的代码。这里我用了三个PHP类,如代码4所示。

  代码4 Data.php

<?php
class Day
{
var $low;
var $high;
var $close;

public function __construct( $low, $high, $close )
{
$this->low = $low;
$this->high = $high;
$this->close = $close;
}
}

class Trace
{
var $days = array();
var $high = 0.0;
var $low = 100000.0;

public function addDay( $low, $high, $close )
{
$this->days []= new Day( $low, $high, $close );
if ( $low < $this->low ) $this->low = $low;
if ( $high > $this->high ) $this->high = $high;
}

public function hiloPath( $trans )
{
$p = new Path();
$d = 0;
foreach( $this->days as $day )
{
$x = $trans->xscale( $d );
$y = $trans->yscale( $day->low );
$p->add( $x, $y );
$d += 1;
}
for( $d = (count( $this->days ) - 1); $d >= 0; $d -= 1 )
{
$x = $trans->xscale( $d );
$y = $trans->yscale( $this->days[$d]->high );
$p->add( $x, $y );
}
return $p;
}

public function closePath( $trans )
{
$p = new Path();
$d = 0;
foreach( $this->days as $day )
{
$x = $trans->xscale( $d );
$y = $trans->yscale( $day->close );
$p->add( $x, $y );
$d += 1;
}
return $p;
}
}

class Data
{
var $traces = array();
var $high = 0;
var $low = 100000;

function parseXML( $file )
{
$data_dom = new DomDocument();
$data_dom->load( $file );

$elStocks = $data_dom->getElementsByTagName( 'stock' );
foreach( $elStocks as $stock )
{
$trace = new Trace();

$days = $stock->getElementsByTagName( 'day' );
foreach( $days as $day )
{
$trace->addDay( (float)$day->getAttribute('low'),
(float)$day->getAttribute('high'),
(float)$day->getAttribute('close') );
}

$this->traces []= $trace;

if ( $trace->high > $this->high ) $this->high = $trace->high;
if ( $trace->low < $this->low ) $this->low = $trace->low;
}
}
}
?>

  Data类包含文件中的所有数据。每支股票存储在一个Trace对象中。每Trace对象包含一组Day对象,定义了当天的最高价、最低价和收盘价。此外,Trace对象知道如何为图像中的最高、最低和收盘价部分创建Path对象。
  Path对象是一个PHP类,是我自己为了方便创建SVG路径专门编写的。这个帮助器类在svg.php文件中定义,如代码5所示。

  代码5 Svg.php

<?php
interface ITransform
{
function xscale( $x );
function yscale( $x );
}

class Transform implements ITransform
{
protected $ox;
protected $oy;
protected $xscale;
protected $yscale;
protected $xoffset = 0;
protected $yoffset = 0;

public function xscale( $x ) {
return $this->ox + ( ( $x - $this->xoffset ) * $this->xscale );
}

public function yscale( $y ) {
return $this->oy - ( ( $y - $this->yoffset ) * $this->yscale );
}
}

class Point
{
var $x;
var $y;
public function __construct( $x, $y )
{
$this->x = $x;
$this->y = $y;
}
}

class Path
{
private $points = array();

public function add( $x, $y )
{
$this->points []= new Point( $x, $y );
}

public function toSVG()
{
$svg = "";
$svg .= "M ".$this->points[0]->x." ".$this->points[0]->y." ";
foreach( $this->points as $pt ) {
$svg .= "L ".$pt->x." ".$pt->y." ";
}
return $svg;
}
}
?>

  这里定义了两个类,都很容易理解:Point和Path。Point类表示一个X-Y值对,Path类是一组Points。ITransform接口和Transform基类更有意思。为了绘制股票图像,必须将美元和每日数据的尺度转化成SVG视图坐标。ITransform接口就是为了完成这项任务而定义的,但是不仅仅完成该任务。

4 绘制图像

  我将分阶段逐步完成整个图像。

4.1 版本1:绘制收盘价路径

  第一个版本中仅仅绘制收盘价路径,这样可以看出data.xml文件中的两支股票在31天中的走势。代码6显示了第一个版本的代码。

  代码6 graph.php的第一个版本

<?php
require_once( 'svg.php' );
require_once( 'data.php' );

class GraphTransform extends Transform
{
public function __construct( $ox, $oy, $width,
$height, $low, $high, $days )
{
$this->ox = $ox;
$this->oy = $oy;
$this->xscale = $width / ( $days - 1 );
$this->yscale = $height / ( $high - $low );
$this->yoffset = $low;
}
}

$d = new Data();
$d->parseXML( 'data.xml' );

$ystart = (int)$d->low - 2;
$yend = (int)$d->high + 2;

$gt = new GraphTransform( 20, 90, 80, 80,
$ystart, $yend, count( $d->traces[0]->days ) );

header( "Content-type: text/xml" );
echo( "<?xml version=\"1.0\" standalone=\"no\"?>\n" );
?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/SVG/DTD/svg10.dtd">
<svg style="shape-rendering:geometricPrecision;"
viewBox="0 0 100 100" xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid meet">

<style type="text/css">
.close { fill-opacity: 0; stroke-opacity:0.8;
stroke-width: 0.4; }
</style>

<?php
foreach( $d->traces as $trace )
{
$closePath = $trace->closePath( $gt );
$closeSVG = $closePath->toSVG();
$color = "#ff0000";
?>
<path fill-rule="nonzero"
d="<?php echo( $closeSVG ); ?>" class="close"
fill="<?php echo( $color ); ?>"
stroke="<?php echo( $color ); ?>"
/>
<?php
}
?>
</svg>

  文件的一开始创建了ITransform接口的一个版本,它把美元值沿着Y轴、当日价格沿着X轴转化成SVG的点。然后在文件的后面,使用foreach迭代器遍历数据中的轨迹列表。对于每个轨迹,使用SVG Path ($closeSVG)获得收盘价,并将该值使用toSVG()方法转化成SVG绘图命令。然后创建一个SVG <path>标记,并使用PHP echo命令写入绘图命令以及fill和color值。

  浏览引用该图像的.html文件时得到了如图3所示的结果。

图3 第一个版本

  看起来不怎么样,但它是正确的,这仅仅是开始。

4.2 版本2:撑起收盘线

  下一步要用稍微亮一点的图形撑起收盘线,它表示当日的最高价和最低价。这样可以让客户了解股票价格的变化。
  为此修改了PHP绘图文件,如代码7所示。

  代码7 graph.php的第二个版本

<style type="text/css">
.close { fill-opacity: 0; stroke-opacity:0.8; stroke-width: 0.4; }
.hilo { fill-opacity: 0.2; stroke-opacity:0.8; stroke-width: 0.1; }
</style>

<?php
$colors = array( '#ff0000','#0000ff','#00ff00' );
$c = 0;
foreach( $d->traces as $trace )
{
$closePath = $trace->closePath( $gt );
$closeSVG = $closePath->toSVG();
$hiloPath = $trace->hiloPath( $gt );
$hiloSVG = $hiloPath->toSVG();
$color = $colors[$c];
$c += 1;
?>
<path fill-rule="nonzero"
d="<?php echo( $hiloSVG ); ?>" class="hilo"
fill="<?php echo( $color ); ?>"
stroke="<?php echo( $color ); ?>"
/>
<path fill-rule="nonzero"
d="<?php echo( $closeSVG ); ?>" class="close"
fill="<?php echo( $color ); ?>"
stroke="<?php echo( $color ); ?>"
/>
<?php
}
?>

  与原来的收盘线基本类似,只不过使用了hiloPath。然后我使用close类创建了另一条路径。要注意SVG如何运用了将级联样式表(CSS)类应用于不同图形对象的思想。可以在类级别或者实例级别设置笔触、透明度、填充以及其他任何图形样式,如果需要甚至可以使用脚本随时改变。图4显示了修改后的版本。

图4 显示了最低价和最高价的图像

  现在可以看到股价的变动了。如果仔细观察图像的开始部分,会看到最低价和最高价图形的交叠,就是说透过红色的踪迹仍然能看到蓝色的踪迹。

4.3 版本3:添加边框和文本

  下一个版本中增加了边框和表明美元价格的文本。变化如代码8所示。

  代码8 graph.php的第三个版本

<style type="text/css">
.close { fill-opacity: 0; stroke-opacity:0.8; stroke-width: 0.4; }
.hilo { fill-opacity: 0.2; stroke-opacity:0.8; stroke-width: 0.1; }
.range { text-anchor: end; font-size: 5pt; }
</style>
...
<line x1="18" y1="10" x2="20" y2="10"
stroke="black" stroke-width="0.2" />
<line x1="20" y1="10" x2="20" y2="92"
stroke="black" stroke-width="0.2" />
<line x1="18" y1="90" x2="100" y2="90"
stroke="black" stroke-width="0.2" />
<line x1="100" y1="90" x2="100" y2="92"
stroke="black" stroke-width="0.2" />
<text x="18" y="12" class="range"><?php echo($yend); ?>
</text>
<text x="18" y="92" class="range"><?php echo($ystart); ?>
</text>
</svg>

  呈现图像的PHP代码没有变。仅仅在Y轴上画了几条线和一些文本。修改后的版本如图5所示。

图5 带有边框和Y轴坐标值的图像

4.4 版本4:添加背景

  最后一步在数据后面增加了一点渐变填充的背景色来突出数据。代码9表明添加这种填充效果是多么简单。

  代码9 渐进填充

<svg style="shape-rendering:geometricPrecision;"
viewBox="0 0 100 100" xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid meet">

<defs>
<linearGradient id="BackGradient"
gradientUnits="objectBoundingBox"
gradientTransform="rotate(90)">
<stop offset="0%" stop-color="#ccc"/>
<stop offset="50%" stop-color="white"/>
<stop offset="100%" stop-color="#ccc"/>
</linearGradient>
</defs>

<style type="text/css">
.close { fill-opacity: 0; stroke-opacity:0.8; stroke-width: 0.4; }
.hilo { fill-opacity: 0.2; stroke-opacity:0.8; stroke-width: 0.1; }
.range { text-anchor: end; font-size: 5pt; }
.background { stroke-width: 0; fill:url(#BackGradient); }
</style>

<rect x="20" y="10" width="80" height="80" class="background" />
...

  代码中的新类background使用了BackGradient。linearGradient填充在浅灰到白色之间有三站,然后再过渡到浅灰。结果如图6所示。

最终的SVG实例图像

  现在举例说明SVG中的缩放,我稍微修改了页面,增加了<embed>标记的宽和高。结果如图7所示。

图7 对最终的图像实例进行放大的效果

  各处都得到了完美的放大,没有任何锯齿。是不是棒极了?......我告诉您,太棒了。我特别喜欢SVG对单位的处理,它非常适合程序员,无需直接使用像素。

5 制作SVG的其他方法

  显然,PHP不是创建.svg文件的惟一手段。任何编程语言 -- -- C#、C、Java语言、Perl -- -- 都能创建SVG文本文件。Perl的Comprehensive Perl Archive Network (CPAN) 库中有一个SVG模块。PHP有支持SVG的PEAR模块,使用Java的人可以考虑Apache Foundation的Batik SVG Toolkit。
  另一种替代语言是可扩展样式表语言转换(XSLT),很容易将XML数据文件转换成.svg文件。ChartSVG是一个开放源代码的绘图项目,使用以XSLT编写的SVG。
  作为前端工具,Adobe Illustrator和GoLive可以创建.svg文件。GNU Image Manipulation Program (GIMP)也支持从路径导出SVG。我最喜欢的绘图程序OmniGraffle 4.0也支持导出SVG。至于科学应用程序,Mathematica有一个导出SVG的扩展。

6 结束语

  Firefox 1.5中增加的SVG,即便非常基本,仍然为Web设计者打开了一个新的世界。Firefox的确不如Internet Explorer普及,但是它拥有不断增长的技术社区所特有的共享情结。共享为其他制造商提供了强大的动力,在其浏览器中包含更好的SVG支持,打开了为客户创造更丰富的图像体验的大门。

7 下载

描述大小下载方法
Contains start.html and start.svg1KBHTTP
The data.xml1KBHTTP
The graph.php2KBHTTP

8 参考资料

  * 您可以参阅本文在developerWorks全球网站上的英文原文
  * PHP主页:PHP主页包含大量关于这种脚本语言的资料。
  * W3C XML页面:万维网联盟(W3C))XML页面是进一步学习XML和相关标准的好去处。
  * SVG标准:W3C也接纳了SVG标准。
  * Adobe的SVG网站:SVG的优秀资源站点,提供了丰富的信息。
  * Mozilla SVG Project:Mozilla SVG Project解释了Firefox支持什么不支持什么。
  * SVG.org:该SVG主页包括关于SVG的新闻和信息。
  * SVG中国:SVG的中文网站,提供了丰富的中文化SVG信息。

9 关于作者

  Jack D Herrington ( 这个 E-mail 地址已经被防止灌水恶意程序保护,您需要激活 Java Script 才能观看 ),高级软件工程师,Leverage Software Inc.
  他是一位具有20多年经验的资深软件工程师。曾撰写了三本书:Code Generation in Action、Podcasting Hacks 和PHP Hacks,此外还有30多篇文章。

(THE END)

 

将要更新