Jashwant
Jashwant

Reputation: 28995

Raphael js. Fill color along a curve

I have created a circle in which I can choose two points along the circumference of of circle.

I want to fill the portion between those two points.

Demo

If you see the demo, I want to fill the angle between two points.

JS:

(function (Raphael) {
    Raphael.colorwheel = function (x, y, size, initcolor, element) {
        return new ColorWheel(x, y, size, initcolor, element);
    };
    var pi = Math.PI,
        doc = document,
        win = window,
        ColorWheel = function (x, y, size, initcolor, element) {
            size = size || 200;
            var w3 = 3 * size / 200,
                w1 = size / 200,
                fi = 1.6180339887,
                segments = 3,//pi * size / 50,
                size20 = size / 20,
                size2 = size / 2,
                padding = 2 * size / 200,
                t = this;

            var H = 1, S = 1, B = 1, s = size - (size20 * 4);
            var r = element ? Raphael(element, size, size) : Raphael(x, y, size, size),
                xy = s / 6 + size20 * 2 + padding,
                wh = s * 2 / 3 - padding * 2;
            w1 < 1 && (w1 = 1);
            w3 < 1 && (w3 = 1);



            // ring drawing
            var a = pi / 2 - pi * 2 / segments * 1.3,
                R = size2 - padding,
                R2 = size2 - padding - size20 * 2,
                path = ["M", size2, padding, "A", R, R, 0, 0, 1, R * Math.cos(a) + R + padding, R - R * Math.sin(a) + padding, "L", R2 * Math.cos(a) + R + padding, R - R2 * Math.sin(a) + padding, "A", R2, R2, 0, 0, 0, size2, padding + size20 * 2, "z"].join();

            for (var i = 0; i < segments; i++) {
                r.path(path).attr({
                    stroke: "none",
                    fill: "#8fd117",
                    transform: "r" + [(360 / segments) * i, size2, size2]
                });
            }

            r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0", "M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
                "stroke-width": w3,
                stroke: "#fff"
            });

            t.startCursor = r.set();
            var h = size20 * 2 + 2;
            t.startCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
                stroke: "#00A0C6",
                opacity: .5,
                "stroke-width": w3
            }));
            t.startCursor.push(t.startCursor[0].clone().attr({
                stroke: "#00A0C6",
                opacity: 1,
                "stroke-width": w1
            }));


            t.endCursor = r.set();
            var h = size20 * 2 + 2;
            t.endCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
                stroke: "#F96E5B",
                opacity: .5,
                "stroke-width": w3
            }));

            t.endCursor.push(t.endCursor[0].clone().attr({
                stroke: "#F96E5B",
                opacity: 1,
                "stroke-width": w1
            }));



            t.ring = r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
                fill: "#000",
                opacity: 0,
                stroke: "none"
            }); 

            t.H = t.S = t.B = 1;
            t.raphael = r;
            t.size2 = size2;
            t.wh = wh;
            t.x = x;
            t.xy = xy;
            t.y = y;


            t.endCursor.attr({transform: "r" + [50, t.size2, t.size2]});

            // events
            t.ring.drag(function (dx, dy, x, y) {
                t.docOnMove(dx, dy, x, y);
            }, function (x, y) { 
                // Rotate on click 
                t.setH(x - t.x - t.size2, y - t.y - t.size2);
            }, function () { 
            }); 
        },
        proto = ColorWheel.prototype;

    proto.setH = function (x, y) { 

        var d = Raphael.angle(x, y, 0, 0); 

        this.H = (d + 90) / 360;
        var a  = 0;

        if(d > 270) {
            d = d - 270;
        }
        else {
            d = d + 90;
        } 

        var m = Math.abs(d - this.startCursor[0]._.deg);
        var n = Math.abs(d - this.endCursor[0]._.deg);

        if(m > 180) {
            m = 360 - m ;
        }
        if(n > 180) {
            n = 360 - n;
        }    

        if( m <= n) {
            this.startCursor.attr({transform: "r" + [d, this.size2, this.size2]});
        }
        else {
            this.endCursor.attr({transform: "r" + [d, this.size2, this.size2]});
        }

        m = this.startCursor[0]._.deg ;
        n = this.endCursor[0]._.deg;

        if(m > 360) {
            m = m - 360;
        }
        if( n > 360 ) {
            n = n - 360;
        } 

        var diff = m > n ? m - n : n - m;

        this.onchange(m,n,diff);
    };

    proto.docOnMove = function (dx, dy, x, y) {
        this.setH(x - this.x - this.size2, y - this.y - this.size2);

    };

})(window.Raphael);



window.onload = function () {
    var cp2 = Raphael.colorwheel(60, 20, 200, "#eee");
    var X = document.getElementById('x');
    var Y = document.getElementById('y');
    var angle = document.getElementById('angle');

    cp2.onchange = function (x, y, ang) {
        X.innerHTML = Math.round(x * 100) / 100;
        Y.innerHTML = Math.round(y * 100) / 100;
        angle.innerHTML = Math.round(ang * 100) / 100;
    }
};

HTML:

<div id="wrapper">X : <span id="x">0</span>
    <br>Y: <span id="y">50</span> 
    <br>Angle: <span id="angle">50</span> 
</div>

CSS:

  body {
      background: #e6e6e6;
  }
  #wrapper {
      position: absolute;
      top: 240px;
      left: 100px;
  }

UPDATE:

With Chris's help,

I have got some success.

See Demo

Bugs :
1. If you start green first, red breaks,
2. If you start red first and makes angle of greater than 180 degree and when green reduces that below 180 degree, it breaks again.

UPDATE 2
DEMO
BUGS:
1. If you start red first and makes angle of greater than 180 degree and when green reduces that below 180 degree, it breaks again.
2. Sometimes arcs in opposite direction.

Upvotes: 3

Views: 1132

Answers (2)

Jashwant
Jashwant

Reputation: 28995

Here's working solution:

Demo

(function (Raphael) {
    Raphael.colorwheel = function (x, y, size, initcolor, element) {
        return new ColorWheel(x, y, size, initcolor, element);
    };
    var pi = Math.PI,
        doc = document,
        win = window,
        ColorWheel = function (x, y, size, initcolor, element) {
            size = size || 200;
            var w3 = 3 * size / 200,
                w1 = size / 200,
                fi = 1.6180339887,
                segments = 3,//pi * size / 50,
                size20 = size / 20,
                size2 = size / 2,
                padding = 2 * size / 200,
                t = this;

            var H = 1, S = 1, B = 1, s = size - (size20 * 4);
            var r = element ? Raphael(element, size, size) : Raphael(x, y, size, size),
                xy = s / 6 + size20 * 2 + padding,
                wh = s * 2 / 3 - padding * 2;
            w1 < 1 && (w1 = 1);
            w3 < 1 && (w3 = 1);

            // ring drawing
            var a = pi / 2 - pi * 2 / segments * 1.3,
                R = size2 - padding,
                R2 = size2 - padding - size20 * 2,
                path = ["M", size2, padding, "A", R, R, 0, 0, 1, R * Math.cos(a) + R + padding, R - R * Math.sin(a) + padding, "L", R2 * Math.cos(a) + R + padding, R - R2 * Math.sin(a) + padding, "A", R2, R2, 0, 0, 0, size2, padding + size20 * 2, "z"].join();
            for (var i = 0; i < segments; i++) {
                r.path(path).attr({
                    stroke: "none",
                    fill: "#8fd117",
                    transform: "r" + [(360 / segments) * i, size2, size2]
                });
            }

            r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0", "M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
                "stroke-width": w3,
                stroke: "#fff"
            });


            t.startCursor = r.set();
            var h = size20 * 2 + 2;

            t.startCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
                stroke: "#00A0C6",
                opacity: 1,
                "stroke-width": w3
            }));
            t.startCursor.push(t.startCursor[0].clone().attr({
                stroke: "#00A0C6",
                fill : "#8fd117", 
                opacity: 1,
                "stroke-width": w1
            }));

            t.endCursor = r.set();
            var h = size20 * 2 + 2;

            t.endCursor.push(r.rect(size2 - h / fi / 2, padding - 1, h / fi, h, 3 * size / 200).attr({
                stroke: "#F96E5B",
                opacity: 1,
                "stroke-width": w3,

            }));

            t.endCursor.push(t.endCursor[0].clone().attr({
                stroke: "#F96E5B",
                fill : "#8fd117",
                opacity: 1,
                "stroke-width": w1
            }));

            t.ring = r.path(["M", size2, padding, "A", R, R, 0, 1, 1, size2 - 1, padding, "l1,0M", size2, padding + size20 * 2, "A", R2, R2, 0, 1, 1, size2 - 1, padding + size20 * 2, "l1,0"]).attr({
                fill: "#000",
                opacity: 0,
                stroke: "none"
            }); 

            t.H = t.S = t.B = 1;
            t.raphael = r;
            t.size2 = size2;
            t.wh = wh;
            t.x = x;
            t.xy = xy;
            t.y = y;

            t.endCursor.attr({transform: "r" + [50, t.size2, t.size2]});

            t.x0 = t.startCursor[0].attr("x") + t.startCursor[0].attr("width") / 2;
            t.y0 = t.startCursor[0].attr("y") + t.startCursor[0].attr("height") / 2;
            t.initX0 = t.x0;
            t.initY0 = t.y0;
            t.R1 = (R2 + R) / 2;
            t.x1 = t.x0 + t.R1 * Math.sin(50 * Math.PI / 180);
            t.y1 = t.y0 + t.R1 - t.R1 * Math.cos(50 * Math.PI / 180);
            t.initX1 = t.x1;
            t.initY1 = t.y1;
            var path = "M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " 50 0,1 " + t.x1 + "," + t.y1;

            t.arc = r.path(path)
            .attr({
                stroke: "#009900",
                "stroke-width": 10
            });


            t.startCursor.drag(function (dx, dy, x, y) {
                   t.docOnMove(dx, dy, x, y,'startCursor');
                }, function (x, y) { 
                    t.setH(x - t.x - t.size2, y - t.y - t.size2,'startCursor');
                }, function () { 

            }); 

            t.endCursor.drag(function (dx, dy, x, y) {
                   t.docOnMove(dx, dy, x, y,'endCursor');
                }, function (x, y) { 
                    t.setH(x - t.x - t.size2, y - t.y - t.size2,'endCursor');
                }, function () { 

            }); 

            t.startCursor.toFront();
            t.endCursor.toFront();
        },
        proto = ColorWheel.prototype;

    proto.setH = function (x, y,cursor) {  
        var d = Raphael.angle(x, y, 0, 0);
        if(d > 270) {
            d = d - 270;
        }
        else {
            d = d + 90;
        } 

        if((cursor === 'startCursor' && d > this.endCursor[0]._.deg) || (cursor === 'endCursor' && d <= this.startCursor[0]._.deg)) {
            return;
        }

        if(cursor === 'startCursor') {
            this.startCursor.attr({transform: "r" + [d, this.size2, this.size2]});
        }
        else {
            this.endCursor.attr({transform: "r" + [d, this.size2, this.size2]});
        }   

        var m = this.startCursor[0]._.deg ;
        var n = this.endCursor[0]._.deg;
        var t = this;        
        var flag = 0;

        if(m > 360) {
            m = m - 360;
            flag = 1;  
        }

        if( n > 360 ) {
            n = n - 360;
        } 


        var diff = Math.abs(m - n); 

        if (diff > 180) { 
            flag = 1;
        }

        var path = "";
        var sweep = 1; 

        if(cursor === 'endCursor') { 
            t.x1 = t.initX0 + t.R1 * Math.sin(n * Math.PI / 180);
            t.y1 = t.initY0 + t.R1 - t.R1 * Math.cos(n * Math.PI / 180);
        }
        else { 
            t.x0 = t.initX0 + t.R1 * Math.sin(m * Math.PI / 180);
            t.y0 = t.initY0 + t.R1 - t.R1 * Math.cos(m * Math.PI / 180);
        }

        console.log(m,t.x0,t.y0,t.x1,t.y1);

        path = "M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " " +  diff + " " +  flag + "," + sweep + " " + t.x1 + "," + t.y1;

        t.arc = t.arc.attr("path", path );

        this.onchange(m,n,diff);
    };

    proto.docOnMove = function (dx, dy, x, y,cursor) {
        this.setH(x - this.x - this.size2, y - this.y - this.size2,cursor);

    };

})(window.Raphael);

window.onload = function () {
    var cp2 = Raphael.colorwheel(60, 20, 200, "#eee");
    var X = document.getElementById('x');
    var Y = document.getElementById('y');
    var angle = document.getElementById('angle');

    cp2.onchange = function (x, y, ang) {
        X.innerHTML = Math.round(x * 100) / 100;
        Y.innerHTML = Math.round(y * 100) / 100;
        angle.innerHTML = Math.round(ang * 100) / 100;


    }
};

Upvotes: 4

Chris Wilson
Chris Wilson

Reputation: 6719

Cool project. You just need to add an elliptical arc to the color wheel and redraw the path on the onchange event.

I got you half the way here: It works if you move the orange cursor, completely breaks if you move the blue cursor.

To start:

        t.x0 = t.startCursor[0].attr("x") + t.startCursor[0].attr("width") / 2;
        t.y0 = t.startCursor[0].attr("y") + t.startCursor[0].attr("height") / 2;
        t.R1 = (R2 + R) / 2;
        t.x1 = t.x0 + t.R1 * Math.sin(50 * Math.PI / 180);
        t.y1 = t.y0 + t.R1 - t.R1 * Math.cos(50 * Math.PI / 180);

        t.arc = r.path("M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " 50 0,1 " + t.x1 + "," + t.y1)
        .attr({
            stroke: "#009900",
            "stroke-width": 10
        });

On update:

    if (n > 180) {
        flag = 1;
    }

    var diff = m > n ? m - n : n - m;

    t.x0 = t.x0 + t.R1 * Math.sin(m * Math.PI / 180);
    t.y0 = t.y0 + t.R1 - t.R1 * Math.cos(m * Math.PI / 180);
    t.x1 = t.x0 + t.R1 * Math.sin(diff * Math.PI / 180);
    t.y1 = t.y0 + t.R1 - t.R1 * Math.cos(diff * Math.PI / 180);

    t.arc = t.arc.attr("path", "M" + t.x0 + "," + t.y0 + "A" + t.R1 + "," + t.R1 + " " +  diff + " " +  flag + ",1 " + t.x1 + "," + t.y1);

jsfiddle

Should be able to take it from here.

UPDATE, May 8:

You can fix your first problem by changing the flag on the diff, not on the second angle:

    if (diff > 180) { 
        flag = 1;
    }

The event that's triggering the second problem is the second angle (the red handle) passing the 0-degree mark. The easiest way to catch this is just to add 360 to the angle IF it's less than the first angle:

    var m = this.startCursor[0]._.deg ;
    var n = this.endCursor[0]._.deg;
    var t = this;        
    var flag = 0;
    var sweep = 1; 

    var path = "";
    if (n < m) {
       m += 360;
    } 

    var diff = Math.abs(m - n); 

    if (diff > 180) { 
        flag = 1;
    }

Here's the fiddle

Note: You were catching situations where (n > 360) and (m > 360), but this doesn't appear necessary -- the angles arrive at this point in the code already set below 360, at least in Chrome.

Upvotes: 4

Related Questions