在CSS或画布中绘制一条曲线,并沿着它移动圆圈

时间:2016-04-15 02:21:20

标签: html css css3 canvas

今天我获得了一个设计,即沿着曲线移动的圆圈。我创建了一个JSBin,其中包含了迄今为止我用纯css取得的进展,但我觉得我的方向错误。我想也许用帆布会更好,但我不知道从哪里开始。这不只是沿着一条线绘制它也填充了条形。

Fiddle

这是设计:

enter image description here

到目前为止,我与CSS有多接近:

enter image description here

2 个答案:

答案 0 :(得分:9)

以下是沿着曲线(这是一个Cubic Bezier曲线)为您的圆设置动画的方法。

  • 使用画布的context.bezierCurveTo方法绘制曲线。

  • 使用一系列画布的context.lineTo方法关闭彩虹路径。

  • 要仅使用彩虹色填充弯曲路径,可以使用context.clip限制图纸仅显示在路径内。然后,您可以使用context.fillRect填充多色乐队。

  • 使用requestAnimationFrame创建一个动画循环,在您的曲线沿着不断增加的航路点绘制球。

  • 使用De Casteljau's Algorithm

  • 计算曲线上的航点

以下是示例代码和演示:

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>

答案 1 :(得分:3)

MarkE给出了很好的答案(他应得的赏金)但是当我看到De Casteljau的算法并仔细观察时,它让我感到震惊的是数学家编写软件,而不是程序员做数学。

在计算中使用传递的参数作为中间体,可以删除一些操作,从而改进算法。它是相同的数学函数,不超过+/- 1e-14(在Javascript浮点数上尽可能接近)

因缺少一个更好的名称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; 
}

并且需要ctx.quadraticCurveTo

所需的二阶多项式
function quadQ(t, a, b, c){
    a += (b - a) * t;
    b += (c - b) * t;
    return a + (b - a) * t;
}

其中a,b,c是曲线上的x或y点,b为控制点。 t是位置0 <= t <= 1

只是为了兴趣线性版

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

你可以看到它只是一条线。二次曲线由线性插值3(线)组成,立方数为6。

对于这个问题,15%的性能提升是微不足道的,而且无关紧要,但是对于更加强烈的需求,15%非常值得花费额外的代码,更不用说它看起来更好了.LOL