道可叨

Free Will

让 Raphael 的 Path 动起来

Raphaël 是一个很简单实用的 Javascript 矢量图形库。它抽象出一套 API 屏蔽了 SVG 和 VML 之间的差异,做到了对主流浏览器的支持,包括很不给力的 IE6(很可惜,并不支持手机 UC 浏览器)。

Raphaël 对于交互事件也有一定的支持,比如常用的鼠标拖放操作等。可惜在官网上虽然有拖放操作的例子,但例子的写法只对 circle,rect 有效,对 path 却不起作用。经过一番实践,终于了解了 Raphaël 对于拖放支持的原理,找到了一个通用的拖放操作方法,能支持所有的矢量对象,也包括 path。

官方例子之所以对 path 不起作用是因为:path 没有象 circle 里面的 cxcy 属性,要移动 path,只能使用 Path.translate() 方法。还要注意的是,Path 必须要先 fill 才能移动。

仿照官网的例子,下面是一个 Path 移动的代码示例:

var R = Raphael("paper", 300, 260);
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 回调时鼠标位置的位移
  • 我们在 startmove 中不断更新相对位移值,并保存在 this.lastXthis.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.uidraggable 的能力。下面是用利用这个扩展重写的拖放 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();

可以看到,只需要简单的调用 .draggable() 就可以让对象被鼠标拖拽了。