How to drag a shape along a given path
Asked Answered
A

2

14

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>
Aficionado answered 13/9, 2011 at 17:33 Comment(3)
The main idea is to get something similar to animateAlong but dragging instead of animating.Aficionado
Here's a jsFiddle for people to use: jsfiddle.net/8T9NQMcgowan
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.Point
F
33

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);​
Fortner answered 15/10, 2012 at 16:22 Comment(3)
Fuzic if i want to create a pie chart something like [link]shodor.org/interactivate/activities/PieChart/[link] is possible using raphael js library ?Pay
@Fortner - Can you explain how gradSearch works? See: #23812822Scripture
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?Wellwisher
E
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.

Exorable answered 15/10, 2012 at 16:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.