Skip to content Skip to sidebar Skip to footer

Drawing A Curved Line In Css Or Canvas, And Moving Circle Along It

I was given a design today that is a circle moving along a curved line. I created a JSBin with the progress I have made so far with pure css but I feel I'm on the wrong direction.

Solution 1:

Here's how to animate your circle along your curved line (which is a Cubic Bezier Curve).

  • Draw your curve using canvas's context.bezierCurveTo method.

  • Close your rainbow path using a series of canvas's context.lineTo method.

  • To fill only the curved path with your rainbow colors, you can use context.clip to restrict drawings to display only inside the path. Then you can use context.fillRect to fill with your multi-colored bands.

  • Use requestAnimationFrame to create an animation loop that draws your ball at increasing waypoints along your curve.

  • Calculate waypoints along your curve using De Casteljau's Algorithm

Here's example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
   
var colors=[[229,133,50],[251,183,50],[133,206,63],[22,155,116],[26,160,219]];
var points=[35,120,317,511,709,792];
var p0={x:37,y:144};
var p1={x:267,y:143};
var p2={x:651,y:129};
var p3={x:794,y:96};
var waypoints=cubicBezierPoints(p0,p1,p2,p3);
var currentIndex=0;
var radius=10;
//requestAnimationFrame(animate);

// draw the rainbow curve thingfunctiondrawCurve(){
    ctx.save();
    ctx.moveTo(37,144);
    ctx.bezierCurveTo(267,143,651,129,794,96);
    ctx.lineTo(794,158);
    ctx.lineTo(37,158);
    ctx.closePath();
    ctx.fill(); 
    ctx.globalCompositeOperation='source-atop';
    for(var i=0;i<points.length-1;i++){
        var c=colors[i];
        ctx.fillStyle='rgb('+c[0]+','+c[1]+','+c[2]+')';
        ctx.fillRect(points[i],0,points[i+1],ch);
    }
    ctx.restore();    
}
//functiondrawBall(){
    var pt=waypoints[currentIndex];
    ctx.beginPath();
    ctx.arc(pt.x,pt.y,radius,0,Math.PI*2);
    ctx.fillStyle='white';
    ctx.fill();
    ctx.strokeStyle='black'
    ctx.lineWidth=3;
    ctx.stroke();
}

// the animation loopfunctionanimate(){
    ctx.clearRect(0,0,cw,ch);
    drawCurve();
    drawBall();
    ctx.beginPath();
    currentIndex++;
    if(currentIndex<waypoints.length){
        requestAnimationFrame(animate);
    }
}

// calculate the waypointsfunctioncubicBezierPoints(p0,p1,p2,p3){
    var ticksPerSecond=60;
    var seconds=4;
    var totalTicks=ticksPerSecond*seconds;
    var pts=[];
    for(var t=0;t<totalTicks;t++){
        pts.push(getCubicBezierXYatT(p0,p1,p2,p3,t/totalTicks));
    }
    return(pts);
}

// De Casteljau's algorithm which calculates points along a cubic Bezier curve// plot a point at interval T along a bezier curve// T==0.00 at beginning of curve. T==1.00 at ending of curve// Calculating 100 T's between 0-1 will usually define the curve sufficientlyfunctiongetCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
    var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
    var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
    return({x:x,y:y});
}
// cubic helper formula at T distancefunctionCubicN(T, a,b,c,d) {
    var t2 = T * T;
    var t3 = t2 * T;
    return a + (-a * 3 + T * (3 * a - a * T)) * T
    + (3 * b + T * (-6 * b + b * 3 * T)) * T
    + (c * 3 - c * 3 * T) * t2
    + d * t3;
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvasid="canvas"width=820height=200></canvas>

Solution 2:

Great answer by MarkE (and he deserves the bounty) but when I saw the De Casteljau's algorithm and had a close look it struck me as a mathematician writing software, not a programmer doing math.

Using the passed arguments as intermediates in the calculation there are a few operations that can be dropped thus improving the algorithm. It is the same math function diverging by no more than +/- 1e-14 (which in Javascript floating point is as close as it gets)

For want of a better name cubicQ

function cubicQ(t, a, b,c, d){
    a +=(b - a)* t;
    b +=(c- b)* t;
    c+=(d -c)* t;
    a +=(b - a)* t;
    b +=(c- b)* t;
    return a +(b - a)* t; 
}

And incase there is a need for the second order polynomials required by the ctx.quadraticCurveTo

function quadQ(t, a, b,c){
    a +=(b - a)* t;
    b +=(c- b)* t;
    return a +(b - a)* t;
}

Where a,b,c are the x or y points on the curve with b the control point. t is the position 0 <= t <= 1

And just for the interest the linear version

function linearQ(t, a, b){
    return a + (b - a) * t;
}

As you can see it is just a line. The quadratic comprised of linear interpolations 3 (lines), and the cubic is 6.

For this question the 15% increase in performance is trivial and inconsequential, but for more intensive need 15% is well worth the few extra lines of code, not to mention that it just looks better.. LOL

Post a Comment for "Drawing A Curved Line In Css Or Canvas, And Moving Circle Along It"