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. I think maybe this would be better done with canvas but I'm not sure where to begin. This is not just drawing along a line its also filling the bars.
Fiddle
Here is the design:
Here is how close I have got so far with CSS:
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 thing
function drawCurve(){
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();
}
//
function drawBall(){
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 loop
function animate(){
ctx.clearRect(0,0,cw,ch);
drawCurve();
drawBall();
ctx.beginPath();
currentIndex++;
if(currentIndex<waypoints.length){
requestAnimationFrame(animate);
}
}
// calculate the waypoints
function cubicBezierPoints(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 sufficiently
function getCubicBezierXYatT(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 distance
function CubicN(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; }
<canvas id="canvas" width=820 height=200></canvas>
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With