我正在编写一个HTML5图表应用程序,使用qooxdoo作为对象系统和widget工具包,而RaphaelJS作为绘图后端。该图的数据模型包含高级对象,如Item,Line等;这些是作为qooxdoo类实现的,具有位置,尺寸,颜色和其他数据的属性。每个类都能够使用render()方法将其实例呈现给Raphael文件。此时,创造了视觉(拉斐尔术语中的“元素”)。
问题是,在创建Raphael visual之前,应该设置一些属性。在拉斐尔,你不能在没有提供中心坐标和半径的情况下绘制圆;你不能创建一个没有路径定义的路径;如果没有实际文本,则无法创建文本标签,依此类推。此外,在创建视觉后,只能设置一些属性:您不能为不存在的视觉设置颜色,笔触样式等。所以我们可以想象以下工作流程:
var circle = new my.Circle();
circle.set({ x: 10, y: 20, r: 30 }); // can't set color here - no visual yet
circle.render(paper);
circle.set({ stroke: "red", strokeWidth: 5 });
好的,如果我们手动创建对象,我们就可以控制这个工作流程。但是如果整个场景图是从JSON解组(加载保存的图),则无法控制调用序列,并且所有属性都将立即设置。这就是我的Circle类在其members
部分中包含以下内容的原因:
// Setter for stroke
_applyStroke: function(val, old) {
this.element && this.element.attr({ stroke: val });
}
// The same for fill, stroke width, stroke style, arrowhead style etc.
// ...
render: function(paper) {
this.element = paper.circle(this.getX(), this.getY(), this.getR());
this._applyStroke(this.getStroke());
this._applyStrokeWidth(this.getStrokeWidth());
// repeat for each style property
}
有没有什么方法可以用更少的样板?我正在考虑创建虚拟Raphael“元素”以在创建实际元素之前接受样式属性,并在创建之后将虚拟属性提交给实际元素。但是这种方法似乎需要对现有代码进行许多更改。我想知道是否有更优雅的方法来实现这一目标?基于AOP的解决方案是可以接受的,因为AOP在qooxdoo中运行良好。
答案 0 :(得分:0)
我想答案取决于RafaelJS的一些限制,我不太清楚。
如果你的应用程序没问题,可以在my.Circle构造函数中创建元素,其中包含x,y和r的一些默认值。然后,您可以将普通属性apply方法传播到元素属性。 (这会使显式 render()方法失败。
如果这样会过早地渲染元素,并显示其属性的所有更改(您可能希望避免),我会寻找元素的隐藏/显示类型控件。也许你可以在隐藏时构建和更改元素。然后, render()方法只会切换可见性。
如果这一切都不起作用,并且由于某种原因你被迫在 render()方法中创建元素,那么可能无法绕过某种小协议:
使用您对强制属性(例如x,y和r)的任何值,在 render()方法中创建元素,无论是初始值还是用户定义的。< / p>
属于只能在现有元素上设置的属性的apply方法可以将其更改写入内部队列(可能只是属性映射列表),而不是直接写入元素。这样您就可以捕获所有“过早”的属性更改。
然后 render()方法只应用队列中的元素。这将实现您在问题标题中提到的“deferred属性应用程序”。缺点是所有进一步的属性更改只会在下一次 render()调用时生效(这次避免创建元素)。
当然,您可以将它与已有的组合在一起,在尚未创建元素时写入队列,否则直接修改它。