使用捕捉SVG限制边界内的旋转对象

时间:2014-12-03 08:16:55

标签: javascript snap.svg

我设法设置FIDDLE,我举例说明了如何绑定svg边界内的元素。拖动普通元素(没有旋转)时它工作正常。但是当我将它应用于旋转的对象时​​,问题出现了:

var s = Snap("#svgout");
var ROOM_WIDTH = 400;
var ROOM_HEIGHT = 200;
var SVG_BOUNDARY = s.rect(0, 0, ROOM_WIDTH, ROOM_HEIGHT);
SVG_BOUNDARY.attr({
  "stroke": "#FF0000",
  "fill": "#FFFFFF",
  id: "room-svg-boundary"
});
var BEFORE_ROTATION_GETBBOX = [];

s.attr({
  viewBox: "0 0 " + ROOM_WIDTH + " " + ROOM_HEIGHT
});
(function() {

  Snap.plugin(function(Snap, Element, Paper, global) {

    Element.prototype.limitDragging = function(params) {
      this.data('minx', params.minx);
      this.data('miny', params.miny);
      this.data('maxx', params.maxx);
      this.data('maxy', params.maxy);
      this.data('x', params.x);
      this.data('y', params.y);
      this.data('ibb', this.getBBox());
      this.data('ot', this.transform().local);
      this.drag(limitMoveDrag, limitStartDrag);
      return this;
    };

    function limitMoveDrag(dx, dy, posx, posy, ev) {
      var tdx, tdy;
      var sInvMatrix = this.transform().globalMatrix.invert();
      sInvMatrix.e = sInvMatrix.f = 0;
      tdx = sInvMatrix.x(dx, dy);
      tdy = sInvMatrix.y(dx, dy);

      this.data('x', +this.data('ox') + tdx);
      this.data('y', +this.data('oy') + tdy);
      if (this.data('x') > this.data('maxx') - this.data('ibb').width) {
        this.data('x', this.data('maxx') - this.data('ibb').width)
      };
      if (this.data('y') > this.data('maxy') - this.data('ibb').height) {
        this.data('y', this.data('maxy') - this.data('ibb').height)
      };
      if (this.data('x') < this.data('minx')) {
        this.data('x', this.data('minx'))
      };
      if (this.data('y') < this.data('miny')) {
        this.data('y', this.data('miny'))
      };

      this.transform(this.data('ot') + "t" + [this.data('x'), this.data('y')]);
    };

    function limitStartDrag(x, y, ev) {
      this.data('ox', this.data('x'));
      this.data('oy', this.data('y'));
    };
  });

})();

var SETUP_DRAG_MOVEMENT = function(ELEM, disable) {
  if (disable != undefined && disable == true) {
    ELEM.undrag();
  } else {
    var RECT_BOUNDARY = s.getBBox();
    var ELEM_BOX = ELEM.getBBox();
    var min_x = -ELEM_BOX.cx + ELEM_BOX.width / 2;
    var max_x = RECT_BOUNDARY.x2 - ELEM_BOX.x2 + ELEM_BOX.width;
    var min_y = -ELEM_BOX.cy + ELEM_BOX.height / 2;
    var max_y = RECT_BOUNDARY.y2 - ELEM_BOX.y2 + ELEM_BOX.height;

    ELEM.limitDragging({
      x: 0,
      y: 0,
      minx: min_x,
      miny: min_y,
      maxx: max_x,
      maxy: max_y
    });
  }
}

var SETUP_DRAG_MOVEMENT_ROTATED = function(ELEM, disable) {
  if (disable != undefined && disable == true) {
    BEFORE_ROTATION_GETBBOX[ELEM.node.id] = ELEM.getBBox();
    ELEM.undrag();
  } else {
    var RECT_BOUNDARY = s.getBBox();
    console.log(RECT_BOUNDARY);
    var ELEM_BOX = (BEFORE_ROTATION_GETBBOX[ELEM.node.id] != undefined && typeof BEFORE_ROTATION_GETBBOX[ELEM.node.id] == "object") ? BEFORE_ROTATION_GETBBOX[ELEM.node.id] : ELEM.getBBox();

    var min_x = -ELEM_BOX.x;
    var max_x = RECT_BOUNDARY.x2 - ELEM_BOX.x2 + ELEM_BOX.width;
    var min_y = -ELEM_BOX.y;
    var max_y = RECT_BOUNDARY.y2 - ELEM_BOX.y2 + ELEM_BOX.height;

    ELEM.limitDragging({
      x: 0,
      y: 0,
      minx: min_x,
      miny: min_y,
      maxx: max_x,
      maxy: max_y
    });
  }
}

var DBLCLICK_HANDLER = function(ELEM) {
  SETUP_DRAG_MOVEMENT(THIS_RECT, true);
  var THIS_ELEM_BB = ELEM.getBBox();
  var transformProp = new Snap.Matrix();
  transformProp.rotate(45, THIS_ELEM_BB.cx, THIS_ELEM_BB.cy);
  transformProp.add(ELEM.matrix);
  ELEM.transform(transformProp);
  SETUP_DRAG_MOVEMENT_ROTATED(THIS_RECT, false);
}

var myCircle = s.circle(380, 20, 20).attr({
  fill: 'blue'
});
var THIS_CIRCLE_LABEL = s.paper.text(376, 25, "1").attr({
  fill: "#FFFFFF"
});
var THIS_CIRCLE = s.group(myCircle, THIS_CIRCLE_LABEL);
THIS_CIRCLE.attr({
  id: "THIS_CIRCLE"
});
SETUP_DRAG_MOVEMENT_ROTATED(THIS_CIRCLE, false);

var myRect = s.rect(0, 0, 30, 30).attr({
  fill: 'green'
});
var THIS_RECT_LABEL = s.paper.text(11, 19, "2").attr({
  fill: "#FFFFFF"
});
var THIS_RECT = s.group(myRect, THIS_RECT_LABEL);
THIS_RECT.attr({
  id: "THIS_RECT"
});
SETUP_DRAG_MOVEMENT_ROTATED(THIS_RECT, false);

THIS_RECT.node.addEventListener("dblclick", function() {
  DBLCLICK_HANDLER(THIS_RECT);
}, false);
<script src="http://snapsvg.io/assets/js/snap.svg-min.js"></script>
<p>Double click on the rectangle to rotate it and then try to drag. When you drag without rotating, it's perfect (The drag boundary). But while dragging a rotated object, then the problem arises..</p>
<svg id="svgout" height="400" width="600"></svg>

我该如何解决这个问题?

1 个答案:

答案 0 :(得分:0)

因为这包括我的一些旧代码,我想我会回答......

最初,这不会更容易考虑现有的转换。我写了一些更新的代码,可以做到这一点......

不同的主要部分是getInversePoint()函数,它获取到屏幕的逆变换。此外,我已经改变了转换的顺序,因此元素总是先被平移。

Example

Element.prototype.getInversePoint = function( x, y ) {
        var pt = this.paper.node.createSVGPoint();  
        pt.x = x; pt.y = y;
        return pt.matrixTransform( this.paper.node.getScreenCTM().inverse());
}

Element.prototype.limitDrag = function( params ) {
    this.data('dragParams', params );
    this.data('x', params.x); this.data('y', params.y);
    this.drag( limitMoveDrag, limitStartDrag );
    return this;    
};

function limitMoveDrag( xxdx, xxdy, ax, ay ) {
    var tdx, tdy;
    var params = this.data('dragParams');
    var pt = this.getInversePoint( ax, ay );
    var dx = pt.x - this.data('op').x;
    var dy = pt.y - this.data('op').y;

    var ibb = this.data('ibb');
    if( ibb.x + ibb.width + +dx > params.maxx ) 
        { dx = params.maxx - (ibb.x + ibb.width) };

    if( ibb.y + ibb.height + +dy > params.maxy ) 
        { dy = params.maxy - (ibb.y + ibb.height)  };
    if( ibb.x + +dx < params.minx ) { dx = params.minx - ibb.x; };
        if( ibb.y + +dy < params.miny ) { dy = params.miny - ibb.y; };

    this.transform(  "t" + [  dx, dy ] +   this.data('ot').toTransformString());
};

function limitStartDrag( x, y, ev ) {
    this.data('ibb', this.getBBox());
    this.data('op', this.getInversePoint( x, y ));
    this.data('ot', this.transform().localMatrix);
};