简体   繁体   中英

Bezier curve with three handles misplaces the control points

I managed to draw four different curves with the examples of the Raphael library. Now, I would like to create a single curve with multiple handles in it. How do I add more handles in this example.

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Bezier curve</title>
            <style>
        #holder {
            height: 100%;
            left: 100%;
            margin: -100% 0 0 -100%;
            position: absolute;
            top: 100%;
            width: 100%;
        }
        </style>
        <script src='jquery.js'></script>
        <script src="raphael.js"></script>
        <script>
           $('document').ready(function () {
                var r = Raphael("holder", window.innerWidth, window.innerHeight)

                function curve(x, y, ax, ay, bx, by, zx, zy, color) {
                    var path = [["M", x, y], ["C", ax, ay, bx, by, zx, zy]],
                        path2 = [["M", x, y], ["L", ax, ay], ["M", bx, by], ["L", zx, zy]],
                        curve = r.path(path).attr({stroke: color || Raphael.getColor(), "stroke-width": 4, "stroke-linecap": "round"}),
                        controls = r.set(
                            r.path(path2).attr({stroke: "#ccc", "stroke-dasharray": ". ","stroke-width":2}),
                            r.circle(x, y, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(ax, ay, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(bx, by, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(zx, zy, 5).attr({fill: "#9F2200", stroke: "none"})
                        );
                    controls[1].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[0][1] = X;
                        path[0][2] = Y;
                        path2[0][2] = X;
                        path2[0][2] = Y;
                        controls[2].update(x, y);
                    };
                    controls[2].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][3] = X;
                        path[1][2] = Y;
                        path2[1][4] = X;
                        path2[1][2] = Y;
                        curve.attr({path: path});
                        controls[0].attr({path: path2});
                    };
                    controls[3].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][3] = X;
                        path[1][4] = Y;
                        path2[2][5] = X;
                        path2[2][2] = Y;
                        curve.attr({path: path});
                        controls[0].attr({path: path2});
                    };
                    controls[4].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][5] = X;
                        path[1][6] = Y;
                        path2[3][6] = X;
                        path2[3][2] = Y;
                        controls[3].update(x, y);
                    };
                    controls.drag(move, up);
                }
                function move(dx, dy) {
                    this.update(dx - (this.dx || 0), dy - (this.dy || 0));
                    console.log(this.dx,this.dy);
                    this.dx = dx;
                    this.dy = dy;
                }
                function up() {
                    this.dx = this.dy = 0;
                }
                curve(70, 100, 110, 100, 130, 200, 170, 200, "hsb(0, 0, 0)");
                 curve(800, 200, 800, 100, 600, 100, 600, 200, "hsb(0, 0, 0)");  // xp1,yp1,  , , , , xp2,yp2 where (xp1,xp2) & (xp2,yp2) are two end points 

                  curve(500, 200,500, 300, 300, 300, 300, 200, "hsb(0, 0, 0)");  // xp1,yp1,  , , , , xp2,yp2 where (xp1,xp2) & (xp2,yp2) are two end points 

                    curve(920, 100,880, 100, 1020, 200, 980, 200, "hsb(0, 0, 0)"); 

            });
        </script>
    </head>
    <body>
        <div id="holder"></div>
    </body>
</html>

</body>

The link for demo is http://jsfiddle.net/insane36/fddGJ/1/

I edited the code and again tried to put multiple handle to show the main handle in the middle but has some problem and I dont know if I understood the concept behind it. I wanted to create a figure with handle as figure below and be able to manipulate the handles;

在此输入图像描述

The code for three handles is ;

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Bezier curve</title>
            <style>
        #holder {
            height: 100%;
            left: 100%;
            margin: -100% 0 0 -100%;
            position: absolute;
            top: 100%;
            width: 100%;
        }
        </style>
        <script src="raphael.js"></script>
        <script>
           window.onload=function () {
                var r = Raphael("holder", window.innerWidth, window.innerHeight)

                function curve(x1, y1, cx1, cy1, cx2, cy2, x2, y2,cx3,cy3,cx4,cy4, color) {  //zx --x1
                    var path = [["M", x1, y1], ["C", cx1, cy1, cx2, cy2, x2, y2,"S",cx3,cy3,cx4,cy4]],
                        path2 = [["M", x1, y1], ["L", cx1, cy1], ["M", cx2, cy2], ["L", x2, y2],["M", cx3,cy3],['L',cx4,cy4]],
                        curve = r.path(path).attr({stroke: color || Raphael.getColor(), "stroke-width": 4, "stroke-linecap": "round"}),
                        controls = r.set(
                            r.path(path2).attr({stroke: "#ccc", "stroke-dasharray": ". ","stroke-width":2}),
                            r.circle(x1, y1, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(cx1, cy1, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(cx2, cy2, 5).attr({fill: "#9F2200", stroke: "none"}),
                            r.circle(x2, y2, 5).attr({fill: "#9F2200", stroke: "none"}),
                             r.circle(cx3, cy3, 5).attr({fill: "#9F2200", stroke: "none"}),
                              r.circle(cx4, cy4, 5).attr({fill: "#9F2200", stroke: "none"})

                        );
                    controls[1].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[0][9] = X;
                        path[0][2] = Y;
                        path2[0][10] = X;
                        path2[0][2] = Y;
                        controls[2].update(x, y);
                    };
                    controls[2].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][11] = X;
                        path[1][2] = Y;
                        path2[1][12] = X;
                        path2[1][2] = Y;
                        curve.attr({path: path});
                        controls[0].attr({path: path2});
                    };
                    controls[3].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][3] = X;
                        path[1][4] = Y;
                        path2[2][13] = X;
                        path2[2][2] = Y;
                        curve.attr({path: path});
                        controls[0].attr({path: path2});
                    };
                    controls[4].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][5] = X;
                        path[1][6] = Y;
                        path2[3][14] = X;
                        path2[3][2] = Y;
                        controls[3].update(x, y);
                    };
                    controls[5].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][8] = X;
                        path[1][9] = Y;
                        path2[4][15] = X;
                        path2[4][2] = Y;
                        controls[4].update(x, y);
                    };
                      controls[6].update = function (x, y) {
                        var X = this.attr("cx") + x,
                            Y = this.attr("cy") + y;
                        this.attr({cx: X, cy: Y});
                        path[1][10] = X;
                        path[1][11] = Y;
                        path2[5][16] = X;
                        path2[5][2] = Y;
                        controls[5].update(x, y);
                    };

                    controls.drag(move, up);
                }
                function move(dx, dy) {
                    this.update(dx - (this.dx || 0), dy - (this.dy || 0));
                    console.log(this.dx,this.dy);
                    this.dx = dx;
                    this.dy = dy;
                }
                function up() {
                    this.dx = this.dy = 0;
                }
                curve(10, 80, 40, 10, 65,10,150,150,95, 80,  180,180, "hsb(0, 0, 0)");

            };
        </script>
    </head>
    <body>
        <div id="holder"></div>
    </body>
</html>

</body>
</html>

I think I have missed and not arranged properly the control points and the values

Take a look at this fiddle - I think I have it doing what you are looking for. ( Edit: fixed this fiddle so you don't have to specify the reflected control point in the curve() constructor)

I think the key is that the second control point of the middle point on the curve is just a reflection of the first control point (per the SVG documentation ), so you have to sort of 'fake' that control. (Your code did have some issues where your update() functions were trying to update array values that don't exist, like path[1][6] = Y; ... path[1] only has three elements)

If you want the two control points to behave independently (so that the curve would not necessarily be smooth through that point), I think you have to remove the "S" from your path, and change some of the code (here is one like that )

If you want the two control points to be allowed to move different distances from the point, but keep the curve smooth through the point, I think you have to do that by hand. You could start with the second example, but you would have to programatically reflect the angle of the moving control point to the opposite control point, while allowing the distance from the opposite control point to the point on the curve to remain fixed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM