Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
795 views
in Technique[技术] by (71.8m points)

javascript - Snap.svg drag group along path

I need to do something along the lines of this, but I with Snap.svg and with the ability to:

  1. Drag the entire group along the path
  2. Preserving spacing during the drag
  3. Allow group drag from any group item
  4. Support any number of group items
  5. Support various different shaped paths

I started this jsfiddle as a working starting point (and also posted below), but I'm at a loss at how best to attack the problem.

var paper = Snap('#panel');

var path = paper.path('M44.16,44.16 L44.16,44.16 L73.6,14.719999999999999 L132.48,73.6 L14.719999999999999,191.35999999999999 L132.48,309.12 L103.03999999999999,338.55999999999995 L44.16,279.67999999999995 L44.16,279.67999999999995')
.attr({
    stroke: 'gray',
    strokeWidth: 3,
    fill: 'none'
});

var c1 = paper.circle(103.03999999999999, 103.03999999999999, 15);
var c2 = paper.circle(44.16, 161.92, 15);
var c3 = paper.circle(73.6, 132.48, 15);

var cGroup = paper.g();
cGroup.add(c1,c2,c3);
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

This quite a tricky one overall. Here's most of the solution which should at least set you off right as one possible method. For the distance checking, I used the code in the original fiddle, so credit to the person who wrote that, as its potentially tricky (and maybe worthy of its own SO question, I think it will need a tweak though).

fiddle here edit: You'll need to tweak to allow for starting position better.

Drag the circle to start it off, as I haven't set the start positions. You will want to adjust the elements starting positions, depending on whether you will zero offset them or whatever (otherwise you will need to allow for this when moving/transforming). You may also want to check for if the first/last element reaches the end and stops them all, so they all stop if one element reaches the path end.

It works by putting all the of the objects in a set, and attaching a handler to each of them (you could possibly just have one handler on the group, more elegant but may be a bit trickier).

We keep track of each elements index

this.data('index')

So when it comes to moving them along the line, we know where it is in the 'chain' and can offset to compensate, ie the following line...

 var whichDrag = this;
 ....
 mySet.forEach( function( el, i ) {

    var which = whichDrag.data("index") - i;
    pt = path.getPointAtLength(l + (which * spacer ));
    if( !isNaN(pt.x) && !isNaN(pt.x) ) { // check if over end
        el.transform('t' + pt.x + ',' + pt.y );
    };
 } ); 

Complete code...

var paper = Snap('#panel');

var spacer = 70;

var path = paper.path('M44.16,44.16 L44.16,44.16 L73.6,14.719999999999999 L132.48,73.6 L14.719999999999999,191.35999999999999 L132.48,309.12 L103.03999999999999,338.55999999999995 L44.16,279.67999999999995 L44.16,279.67999999999995')
.attr({
    stroke: 'gray',
    strokeWidth: 3,
    fill: 'none'
});
var pt = path.getPointAtLength(l);
    //e = r.ellipse(pt.x, pt.y, 4, 4).attr({stroke: "none", fill: "#f00"}),
var totLen = path.getTotalLength();


var r1 = paper.rect(0,0,10,10);
var c3 = paper.circle(0,0, 15);
var c2 = paper.circle(0,0, 15);
var c1 = paper.circle(0,0, 15);
var l = 0;
var searchDl = 1;

var cGroup = paper.g();
cGroup.add(c3,c2,c1,r1);

var mySet = cGroup.selectAll("*");

start = function () {
    this.data("ox", +this.getBBox().cx );
    this.data("oy", +this.getBBox().cy );
    this.attr({opacity: 1});
},
move = function (dx, dy) {
    var whichDrag = this;
    var tmpPt = {
        x : this.data("ox") + dx, 
        y : this.data("oy") + dy
    };
    // move will be called with dx and dy
    l = gradSearch(l, tmpPt);
    pt = path.getPointAtLength(l);
  //  this.attr({cx: pt.x, cy: pt.y});

    mySet.forEach( function( el, i ) {
        var which = whichDrag.data("index") - i;
        pt = path.getPointAtLength(l + (which * spacer ));
        if( !isNaN(pt.x) && !isNaN(pt.x) ) {
             //el.attr({cx: pt.x, cy: pt.y});
            el.transform('t' + pt.x + ',' + pt.y );
        };
    } );

},
up = function () {
    // restoring state
    this.attr({opacity: 1});
},

gradSearch = function (l0, pt) {
    l0 = l0 + totLen;
    var l1 = l0,
        dist0 = dist(path.getPointAtLength(l0 % totLen), pt),
        dist1,
        searchDir;

    if (dist(path.getPointAtLength((l0 - searchDl) % totLen), pt) > 
       dist(path.getPointAtLength((l0 + searchDl) % totLen), pt)) {
        searchDir = searchDl;
    } else {
        searchDir = -searchDl;
    }

    l1 += searchDir;
    dist1 = dist(path.getPointAtLength(l1 % totLen), pt);
    while (dist1 

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...