13

I've this simple dummy file that I'm using to do some testing. The intended result is to drag the red circle along the path. The thing is that I can't figure out how to associate both shapes.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />    
    <script src="raphael-min.js"></script>    
</head>
<body>    
<script type="text/javascript">    
// Creates canvas 320 × 200 at 10, 50
var r = Raphael(10, 50, 320, 200);

var p = r.path("M100,100c0,50 100-50 100,0c0,50 -100-50 -100,0z").attr({stroke: "#ddd"}),
    e = r.ellipse(104, 100, 4, 4).attr({stroke: "none", fill: "#f00"}),


/*var c = r.circle(100, 100, 50).attr({
    fill: "hsb(.8, 1, 1)",
    stroke: "none",
    opacity: .5
});*/


var start = function () {
    // storing original coordinates
    this.ox = this.attr("cx");
    this.oy = this.attr("cy");
    this.attr({opacity: 1});
},
move = function (dx, dy) {
    // move will be called with dx and dy
    this.attr({cx: this.ox + dx, cy: this.oy + dy});
},
up = function () {
    // restoring state
    this.attr({opacity: 1});
};
e.drag(move, start, up);    
</script>
</body>
</html>
Phrogz
  • 296,393
  • 112
  • 651
  • 745
Guillermo
  • 1,493
  • 3
  • 16
  • 35
  • The main idea is to get something similar to animateAlong but dragging instead of animating. – Guillermo Sep 13 '11 at 17:49
  • 2
    Here's a jsFiddle for people to use: http://jsfiddle.net/8T9NQ/ – Joe Sep 14 '11 at 02:58
  • In general, you need to project the cursor position onto the path, finding the closest point on the path. It is quite possible that there may be two (or more, or infinite) points on the path closest to the cursor, so you will then need to disambiguate to choose the best one. – Phrogz Sep 14 '11 at 02:58

2 Answers2

32

You didn't specify exactly how you want the interaction to work, so I used what feels most natural to me.

We can assume the dot must remain on the path, so its position must be given by

p.getPointAtLength(l);

for some l. To find l we can search for the local minimum of the distance between the curve and the cursor position. We initialize the search with l0 where l0 is the value of l currently defining the location of the dot.

See the JSfiddle here for a working example:

http://jsfiddle.net/fuzic/kKLtH/

Here is the code:

var searchDl = 1;
var l = 0;

// Creates canvas 320 × 200 at 10, 50
var r = Raphael(10, 50, 320, 200);

var p = r.path("M100,100c0,50 100-50 100,0c0,50 -100-50 -100,0z").attr({stroke: "#ddd"}),
    pt = p.getPointAtLength(l);
    e = r.ellipse(pt.x, pt.y, 4, 4).attr({stroke: "none", fill: "#f00"}),
    totLen = p.getTotalLength(),


start = function () {
    // storing original coordinates
    this.ox = this.attr("cx");
    this.oy = this.attr("cy");
    this.attr({opacity: 1});
},
move = function (dx, dy) {
    var tmpPt = {
        x : this.ox + dx, 
        y : this.oy + dy
    };
    l = gradSearch(l, tmpPt);
    pt = p.getPointAtLength(l);
    this.attr({cx: pt.x, cy: pt.y});
},
up = function () {
    this.attr({opacity: 1});
},
gradSearch = function (l0, pt) {
    l0 = l0 + totLen;
    var l1 = l0,
        dist0 = dist(p.getPointAtLength(l0 % totLen), pt),
        dist1,
        searchDir;

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

    l1 += searchDir;
    dist1 = dist(p.getPointAtLength(l1 % totLen), pt);
    while (dist1 < dist0) {
        dist0 = dist1;
        l1 += searchDir;
        dist1 = dist(p.getPointAtLength(l1 % totLen), pt);
    }
    l1 -= searchDir;

    return (l1 % totLen);
},
dist = function (pt1, pt2) {
    var dx = pt1.x - pt2.x;
    var dy = pt1.y - pt2.y;
    return Math.sqrt(dx * dx + dy * dy);
};
e.drag(move, start, up);​
fuzic
  • 2,492
  • 16
  • 22
  • Fuzic if i want to create a pie chart something like [link]http://www.shodor.org/interactivate/activities/PieChart/[link] is possible using raphael js library ? – Jose G Varanam Apr 15 '14 at 12:27
  • @fuzic - Can you explain how gradSearch works? See: http://stackoverflow.com/questions/23812322/snap-svg-determine-drag-distance-along-a-path – Inator May 22 '14 at 16:24
  • Is there any way to convert the drag of this script into a mouse movement? Only on mouse hover, without the need of dragging, it moves? – Henrique M. Feb 24 '16 at 19:39
1

A circle object has an x,y coordinate for its center, and a radius. To make sure the circle remains on the line, simply find the intersection of the center of the circle and the line itself.

To do this, you will need to store the start and end coordinates of your line. Then using the equation of a line: y = mx + b, you can find the slope and y-intercept. Once you have a function for the line, you can generate new coordinates for the circle by plugging in different values of x.

Also, by plugging in the x,y coordinates of the circle into your function, you can check to see if the circle is on the line.

Ross Gatih
  • 649
  • 4
  • 9