Dojo.gfx包分析
2006-12-22
修订:
2007-01-08:更正对attach的描述,改“拷贝”为“引用”
1. 背景
由于目前在主流浏览器之间没有一个标准的WEB2D矢量图形技术,特别是在IE没有内置支持SVG之前,WEB开发人员一直以来都在寻找一个能够跨平台的解决方案。在INTERNET上不难发现很多人都在这方面进行着不懈的努力。其主要的方向是:
一,比较简单的方法是使用javascript实现几种图形技术之间的转换,特别是SVG到VML;
二,通过实现一个与实际渲染器无关的中间层来实现在不同浏览器上的矢量图形显示和处理。
这些实现包括:
其中dojo.gfx就是通过一个抽象图形中间层,实现跨浏览器的图形显示。dojo.gfx在0.4版本中增加,目前还处于发布初期,接口函数和实现方法在将来还可能调整改变,但已经实现了大多数矢量图形的功能(不包括文本,clip和filter等高级功能)。
2. gfx相关文件
作用
|
|
_package__.js
|
|
Common.js
|
抽象图形类,包括surface
|
Shape.js
|
抽象图形类,包括shape(基类),rect,line,ellipse,circle,polyline,image和group
|
Vml.js
|
对抽象图形类的VML相关实现以及对path的重载实现
|
Svg.js
|
对抽象图形类的SVG相关实现
|
Path.js
|
路径类
|
Matrix.js
|
坐标矩阵类
|
Color.js
|
色彩类
|
Colorspace.js
|
不同的色彩表示方法之间的转换
|
Color
文件夹
|
色彩相关设置
|
在目前的版本中没有发现针对Canvas的实现,在dojo的某些文档中提到由于Canvas的实现机制与SVG差别较大,因此被删除了。另外,在dojo.gfx中没有对text对象的支持,由于text实现的难度比较大,可能在后续版本中增加,这对目前的使用不能不说是个缺憾了。
用户在使用gfx包时,仅仅需要关心在Common.js,Shape.js,Path.js和Matrix.js中的抽象接口类,而不需要了解Vml.js和Svg.js中的各抽象类的具体实现。下面就讨论一下抽象接口层中的gfx类及其属性和方法。
3. gfx图形类
Dojo.gfx中的类基本分两种,与渲染器相关的类和与渲染器无关的类。所谓渲染器就是针对不同浏览器支持的矢量图形引擎,例如SVG,VML和Canvas
3.1. 与渲染器相关的类
与渲染器相关的类包括Path,Shape,Group,Surface,其中Shape下包括子类rect,image。这些图形对象都对应一个抽象类和实际渲染器的实现类。以下列出其属性和方法(私有属性和方法命名以“_”开头),可能没有列全,需要的可以查看其JS源代码。
(原文中为UML图形,但现在CSDN BLOG无法上图,只能以后再补了 :( )
dojo.gfx.Shape
setStroke
设置笔画
setFill
设置填充
_applyTransform
坐标变换,与setTransform不同,是一种增量变换
setRawNode
设置节点数据
moveToFront
在图层中移动到最前面
moveToBack
在图层中移动到最后面
setShape
设置图形
attachFill
引用使用已有的填充对象
attachStroke
引用使用已有的笔画对象
attachTransform
引用使用已有的变换矩阵对象
attachShape
引用使用已有的图形形状对象
attach
引用使用已有的图形(包括其笔画,形状,填充和坐标矩阵对象等)
rect
矩形
line
直线
ellipse
椭圆(当rx=ry
时为圆)
circle
圆弧
polyline
多边形
image
光栅图形,可以为BMP,JPG,PNG
和GIF
等
virtualGroup/group
图形组
add
在组中增加图形
remove
在组中删除图形
对组的操作举例
var g1 = surface.createGroup();
var g2 = g1.createGroup();
g1.add(line);
g1.remove(line);
g1.add(g2)
path
路径
setAbsoluteMode
getAbsoluteMode
getBoundingBox
getLastPosition
moveTo
M m
lineTo
L l
hLineTo
H h
vLineTo
V v
curveTo
C
smoothCurveTo
S
qCurveTo
Q
qSmoothCurveTo
T
arcTo
A
closePath
Z
setShape
熟悉SVG或VML对上面的标记也比较了解,就不一一解释了。
3.2. 与渲染器无关的类
填充对象
填充对象描述了图形的不同填充方式。根据填充类型的不同,填充对象的属性数量和种类也不同。Fill 对象现在支持三种填充方式,包括线性渐变填充linearGradientFill,圆形渐变填充radialGradientFill和图案填充PatternFill,其中图案填充不一定被所有浏览器支持(例如目前版本的FireFox)。
l 线性渐变
var lg = {
type: "linear",
x1: 0,y1: 0,x2: 75,y2: 50,
colors: [
{ offset: 0,color: "#F60" },
{ offset: 0.5,color: "#FAF" },
{ offset: 1,color: "#FF6" }
]
};
var ref = surface.createRect(rect)
.setFill(lg)
;
l 圆形渐变
var rg = {
type: "radial",
cx: 100,cy: 100,r: 100,color: "red" },color: "green" },color: "blue" }
]
};
var circle = {cx: 100,r: 100 };
var ref = surface.createCircle(circle)
.setStroke({color: "blue",width: 1 })
.setFill(rg)
.setTransform({dx: 40,dy: 30})
;
l 图案填充
var pattern = {
type: "pattern",
x: 0,y: 0,width: 120,height: 96,
src: "http://dojotoolkit.org/img/viewcvs.png"
};
var ellipse = {cx: 150,rx: 150,ry: 100};
var ref = surface.createEllipse(ellipse)
.setStroke({color: "blue",width: 1 })
.setFill(pattern)
;
笔画对象
笔画对象描述了不同笔画风格,包括颜色,宽度和连接方式。
dojo.gfx.createStroke(color,width,cap,join)
返回一个笔画对象,其中Cap可以为"butt","round",或 "square". Join 可以为"round","bevel",或者一个倾斜数值. Cap 和 join为可选属性。
var points = [ {x:70,y:15},{x:40,y:70},{x:100,y:80},{x:130,y:-20}];
var ref = surface.createPolyline(points)
.setStroke({color:[255,0.7],width:1});
矩阵对象
(图)
通过变换矩阵的操作就可以实现图形的坐标变换,而dojo中的matrix对象就是SVG的变换矩阵,其中xx = a; xy=b; yx=c; yy=d; e=dx; f=dy。
变换矩阵的操作包括:
translate 平移
scale 放大
scaleAt 定点放大
rotate 旋转
rotateg 按角度旋转
rotateAt 定点旋转
rotategAt 按角度定点旋转
skewX X歪斜
skewXg 按角度X歪斜
skewY Y歪斜
skewYg 按角度Y歪斜
skewXAt 定点X歪斜
skewYAt 定点Y歪斜
skewXgAt 定点按角度X歪斜
skewYgAt 定点按角度Y歪斜
normalize 单位化
clone 克隆
invert 翻转
_multiplyPoint 对坐标点的矩阵变换
multiplyPoint 对坐标点的矩阵变换
multiply 矩阵相乘
_sandwich 定点实施矩阵变换
坐标变换应用
var circle = {cx: 100,r: 100 };
var ref = surface.createCircle(circle)
.setTransform({dx: 40,dy: 30});
或者
circle.setTransform(dojo.gfx.matrix.translate(40,30));
4. 使用
4.1. 基本使用
使用dojo.gfx包步骤
1,包含dojo.gfx包
dojo.require("dojo.gfx.*");
2,设置和取得容器节点,一个容器是一个HTML节点,gfx的内容就放置在相应的容器下。
在HTML的BODY中:
<div id="test" style="width: 500px; height: 500px;"></div>
在脚本中:
gTestContainer = dojo.byId("testcontainer");
3,建立画布surface,对SVG对应的是<svg>节点,而VML中为<v:group>,画布类使用基本图形类,dojo会根据浏览器类型自动选择可用的渲染器,并建立相应的surface对象。
surface = dojo.gfx.createSurface(gTestContainer,500,500);
4,建立各种图形对象并设置参数,可以采用两种方式,一是直接使用createObject
dojo.gfx.createObject(dojo.gfx.Line,line);
l 矩形
var rect = { x: 0,width: 100,height: 30 };
var red_rect = surface.createRect(rect);
l 圆
var circle = { cx: 130,cy: 130,r: 50 };
var ref = surface.createCircle(circle)
.setFill([0,255,0.7])
.setTransform({ dx: 20,dy: 20 })
;
或直接传入对象创建(也适用于其它图形)
var aShape = surface.createCircle({cx: cx,cy: cy,r: r})
.setFill(randColor(true))
.setStroke({color: randColor(1),width: getRand(3)})
;
l 直线
var line = { x1: 0,y1:0,x2:80,y2:80 };
var ref = surface.createLine(line)
.setFill([0,0.7])
.setStroke({color:[0,width:3} )
.setTransform({ dx: 20,dy: 20 })
;
l 多边形
var points = [ {x:70,y:-20}];
var ref = surface.createPolyline(points)
.setFill([0,0.7])
.setStroke({color:[255,width:1})
.setTransform({ dx: 50,dy: 20 })
;
l 路径,单独定义在path.js中
var re1 = g1.createPath()
.moveTo(startPoint)
.arcTo(rx,ry,xRotg,true,false,endPoint)
.setStroke({color: "red"})
;
当然也可以使用路径字符串创建路径
var path=surface
.createPath("M100 100 200 100 200 200C200 250 150 250 100 200S50 100 100 100z")
.setStroke({color: "blue"})
.setFill("#ddd")
.setTransform({dx: 100,dy: -50})
l 图像
image = surface.createImage({width: 319,height: 95,
src: "http://dojotoolkit.org/img/header-downloads.png"});
5,图形的引用和删除
//元素
引用节点
this.rawNode = rawNode;
//属性
引用填充
this.fillStyle = this.attachFill(rawNode);
引用线形
this.strokeStyle = this.attachStroke(rawNode);
引用变换矩阵
this.matrix = this.attachTransform(rawNode);
引用图形
this.shape = this.attachShape(rawNode);
var ar = dojo.gfx.attachNode(ref.rawNode);
Gavin Doughtie wrote:
> mySurface.rawNode.removeChild(myPath.rawNode);
>
> should work,but it seems wrong. Euguene -- I don't see API for this.
> What's your suggestion?
>
6,关联事件
各种图形可以关联鼠标和键盘事件,例如在图形上关联鼠标点击事件
image = surface.createImage({width: 319,
src: "http://dojotoolkit.org/img/header-downloads.png"});
dojo.event.connect(image.getEventSource(),"onclick",function(){ alert("You didn't expect a download,did you?"); });
4.2. 图形的拖放
gfx图形的拖放不能使用dojo中的dnd,要通过gfx容器的事件处理完成。
1,设置gfx容器的鼠标处理事件
dojo.event.connect(container,'onmousedown',onGrab);
dojo.event.connect(container,'onmousemove',onDrag);
dojo.event.connect(container,'onmouseup',onDrop);
2,建立图形并设置ID
aShape.getEventSource().setAttribute('shapeid',id);
//要设置相应的CSS属性,这对消除VML移动障碍非常重要
dojo.html.setClass(aShape.getEventSource(),"movable");
gShapes[id] = aShape;
3,根据ID属性得到图形对象
function getShape(event)
{
var id = event.target.getAttribute('shapeid');
var s= id ? gShapes[id] : null;
return s;
}
4,完成处理事件
function onGrab(event)
{
var shape = getShape(event);
if (shape) {
current_shape = shape;
current_shape.moveToFront();
last_position = {
x: event.clientX,
y: event.clientY
};
}
//dojo.event.browser.stopEvent(event);
}
function onDrag(event)
{
if(!current_shape)
return;
var x = event.clientX - last_position.x;
var y = event.clientY - last_position.y;
current_shape.applyTransform({dx: x,dy: y});
last_position = {
x: event.clientX,
y: event.clientY
};
//dojo.event.browser.stopEvent(event);
}
function onDrop(event)
{
current_shape = null;
//dojo.event.browser.stopEvent(event);
}
参考资料
Dojo Book和例子
Dojo Interesting Forum