让 Raphael 的 Path 动起来
Raphaël 是一个很实用的线上矢量图操作 Javascript 库。使用简单,一个值得一提的卖点是通过抽象出共同的接口屏蔽了 SVG 和 VML 之间的差异,做到了对主流浏览器的支持,包括很不给力的 IE6。(很可惜,并不支持手机 UC 浏览器)
Raphael 对于交互事件也有一定的支持,比如常用的鼠标的拖放操作(Drag and Drop)。在官方网站上也有拖放操作的例子。可惜的是,官方的这个例子的写法只对 Circle,Rect 等有效,但对于 Path 却不起作用。经过一番实践,终于了解了 Raphaël 对于拖放支持的原理,想出了一个通用的拖放操作的写法,支持所有的 Raphael 矢量对象,包括 path。
官方例子之所以不能让 Path 动起来是因为:Path 没有象 Circle 里面的 cx 和 cy 属性,要移动 Path,只能使用 Path.translate() 方法。还有一点要注意的是,Path 必须要先 fill 才能移动。
仿照官方的例子,下面是一个 Path 移动的代码示例:
var R = Raphael("paper", 400, 300); var p = R.path('M0 0L100 0L50 80Z'); p.attr({"fill":"green", 'opacity':0.8}); var start = function (x, y) { this.attr({opacity: 1}); this.lastX = x; this.lastY = y; }, move = function (dx, dy, x, y) { var deltaX = x - this.lastX; var deltaY = y - this.lastY; this.translate(deltaX, deltaY); this.lastX = x; this.lastY = y; }, up = function () { this.attr({opacity: 0.8}); }; p.drag(move, start, up);
运行效果如下
上面的例子有几点要说明一下
- 回调函数 move 的第一个和第二个参数永远是鼠标所在的点距移动初始时的鼠标位置的相对位移值。而不是距离上次 move 回调时鼠标位置的相对位移值
- 我们在 start 和 move 中记录并不断更新相对上次鼠标位置的相对位移值,并保存在 this.lastX 和 this.lastY 中
- 我们通过 this.translate() 进行实际的移动操作
- 在一开始,要设置 fill 属性,否则就不能进行移动操作
这个例子不但能工作,更好的是,因为所有的 Raphael 矢量对象都有 translate 方法。所以,它不仅对 path 有效,也对所有的 Raphael 矢量对象有效。那么,是不是能做出来类似 jQuery.ui 里面的 draggble 的函数呢。下面就是一个简单的扩展,为 Raphael 对象加入了 draggable 方法。
(function(R) { R.el.draggable = function(move, start, up) { this._ui = this._ui || {}; var that = this; this._ui.onMove = R.is(move, 'function') ? move : function(distanceX, distanceY, x, y, deltaX, deltaY) { that.translate(deltaX, deltaY); }; this._ui.onStart = R.is(start, 'function') ? start : function(x, y) { }; function onMove(distanceX, distanceY, x, y) { var deltaX = x - that._ui.lastX; var deltaY = y - that._ui.lastY; that._ui.lastX = x; that._ui.lastY = y; that._ui.onMove(distanceX, distanceY, x, y, deltaX, deltaY); that.paper.safari(); }; function onStart(x, y) { that._ui.lastX = x; that._ui.lastY = y; that._ui.onStart(x, y); }; return this.drag(onMove, onStart, up); }; })(Raphael);
上面的一段代码,扩展了 Raphael 对象的方法,让它们拥有了类似 jquery.ui 里 draggable 的能力。下面是用利用这个扩展重新写的拖放 Path 的例子。
var R = Raphael("paper", 400, 300); R.rect(0, 0, 400, 300); var p = R.path('M0 0L100 0L50 80Z'); p.attr({"fill":"green", 'opacity':0.5}); p.draggable();
可以看到,像 jquery.ui 提供的功能一样,只需要简单的调用 .draggable() 就可以让对象被鼠标拖拽