自动选择SVG和VML的WEB页面
问题
在WEB上使用二维图形编程的人员现在面临者一个两难的选择,是使用SVG,还是VML?二者的功能 相似,可以满足大多数WEB二维图形应用的需求,但目前的情况是:VML只能在IE中使用,SVG被firefox,opera等浏览器的最新版本支持, 并且是W3C制定的开放标准,但目前IE不内置支持SVG,只能通过ADOBE的SVG插件显示。
对于有特定用户的应用,可能选择哪一种技术都可以,软件提供方可以要求用户安装和配置 所需的环境。但对于面向INTERNET用户的应用,例如网上地图等,这个问题就比较明显了。IE无疑是最广泛使用的浏览器,而且大多数用户在不了解网站 内容的情况下,不会选择下载并安装一个4M多的SVG插件。而在技术上,SVG无疑前景更光明,而且被其它浏览器支持,可以被非WINDOWS平台用户使 用。
思路
能否通过用户浏览器的类型,自动在SVG和VML之间切换,以在浏览器上显示同样(或基本相似)的图形?(下面提到的SVG都是SVG1.1标准)
解决方法
1, 根据用户浏览器情况,设置显示标志
//设置使用SVG显示标志,缺省为使用
var useSVG = true;
//如果为IE则使用VML
if (navigator.appName == "Microsoft Internet Explorer")
useSVG = false;
//如果为其它浏览器,则使用SVG,这里用Opera
if (navigator.userAgent.search("Opera")>=0)
useSVG = true;
2, 在HTML初始化时,加载SVG或VML对象
if(useSVG)
{
var elem = document_createElement_x_x_x("embed");
elem.id = "svgCanvas";
elem.width = 500;
elem.height = 500;
elem.name = "svgCanvas";
elem.src = "canvas.svg";
elem.wmode = "transparent";
elem.type = "image/svg+xml";
document.body.insertBefore(elem,null);
}
else
{
var elem = document_createElement_x_x_x("v:group");
elem.id = "vmlCanvas";
elem.style.width = 500 + "px";
elem.style.height = 500 + "px";
elem.coordsize = "200,200";
document.body.insertBefore(elem,null);
}
之后,我们就可以分别在SVG或VML画布上进行操作了。当然最好是定义一个与显示无关的图形对象。
3, 图形对象,此处为一个简单的圆
function Bubble(id, cx, cy, r, stroke, fill)
{
this.id = id;
this.cx = cx;
this.cy = cy;
this.r = r;
this.stroke = stroke;
this.fill = fill;
this.draw = draw;
this.move = move;
}
其显示和移动实现为:
function draw()
{
if(useSVG)
{
//alert("svg");
var svgDocument = GetSvgDocument();
if(svgDocument == null)
return;
var elem = svgdocument_createElement_x_x_xNS(svgns, "circle" );
elem.setAttributeNS(null, "id", this.id);
elem.setAttributeNS(null, "cx", this.cx);
elem.setAttributeNS(null, "cy", this.cy);
elem.setAttributeNS(null, "r", this.r);
elem.setAttributeNS(null,"stroke",this.stroke);
elem.setAttributeNS(null,"fill",this.fill);
var svgCanvas = GetSvgCanvas();
if(svgCanvas == null)
return;
svgCanvas.a(elem);
}
else
{
//alert("vml");
var elem = document_createElement_x_x_x("v:oval");
with (elem.style)
{
position="absolute";
top=this.cy - this.r;
left=this.cx - this.r;
width=parseInt(this.r*2);
height=parseInt(this.r*2);
}
elem.coordorigin = "0,0";
elem.strokecolor=this.stroke;
elem.FillColor = this.fill;
vmlCanvas.insertBefore(elem,null);
}
}
function move(x, y)
{
this.cx = x;
this.cy = y;
}
4, 使用
var bubble1 = new Bubble("b1", 100, 100, 30, "purple", "green");
bubble1.draw();
var bubble2 = new Bubble("b2", 30, 20, 20, "red", "yellow");
bubble2.draw();
VML显示:
SVG显示
5,注意事项:
l 通过上面的图可以看出,尽管比较相似,但还是有所不同,例如圆的边线由于使用的是缺省属性,SVG和VML使用的笔画粗细是不同。因此要显示完全相同的图形要付出很大的工作量。
l 尽管SVG和VML在大多数情况下可以互相替换,但还是有一些对方不支持的特性和属性。例如SVG中的动画(animate)等,这些要在VML-SVG混合时尽量避免使用,实在不行就只能使用脚本模拟实现了。
l SVG为独立的嵌入对象,因此要使用embedded列表取得,而VML可以直接使用HTML的文档对象。在SVG动态嵌入时,要判断加载情况,在加载完成后才可以操作。
在SVG中加载事件中通知HTML
function OnLoadEvent(evt)
{
window.parent.svgLoadFlag = true;
}
在HTML初始化时,等待SVG加载完成标志
if(useSVG)
{
//Wait svg load event
if ( svgLoadFlag == false )
{
setTimeout("renderObjects()",100);
return;
}
}
l 如果使用交互事件,例如鼠标操作等,SVG和VML的事件对象结构是不同的,传递方式也不相同,应分别处理,不过一般在得到事件对象的属性,例如坐标位置,产生事件对象ID后,就可以使用相同的处理函数了。例如:
//SVG的事件处理
function svgClick(evt)
{
var choiceId = evt.target.getAttributeNS(null,"id");
selectChoice(choiceId);
}
//VML事件处理
function vmlClick()
{
var choiceId = event.srcElement.name;
selectChoice(choiceId);
}
因此在VML/SVG混合编程时,采用MODEL-VIEW-CONTROL方式是比较好的一种模式。