【问题标题】:Drawing a curved line in CSS or canvas, and moving circle along it在 CSS 或画布中绘制一条曲线,并沿着它移动圆圈
【发布时间】:2016-08-06 20:09:44
【问题描述】:

我今天得到了一个设计,它是一个沿曲线移动的圆。我创建了一个 JSBin,其中包含迄今为止我在纯 css 方面取得的进展,但我觉得我走错了方向。我认为使用画布可能会更好,但我不确定从哪里开始。这不仅仅是沿着一条线绘制,它还填充了条形图。

Fiddle

这是设计:

这是到目前为止我与 CSS 的接近程度:

【问题讨论】:

  • 有人请重新打开它,以便我可以添加赏金和奖励@markE
  • 仍然是 this answer 的副本吗?赏金本可以授予原始答案仅供参考。
  • 它有一些相似之处,但填充路径对我来说也非常具有挑战性。有了这部分,答案就完成了。

标签: html css canvas


【解决方案1】:

以下是沿曲线(即三次贝塞尔曲线)为圆设置动画的方法。

  • 使用画布的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; }
&lt;canvas id="canvas" width=820 height=200&gt;&lt;/canvas&gt;

【讨论】:

  • 经过测试和验证。但是我几天内不会接受它,因为我想增加赏金并给你。这是我收到的最好的答案。不仅通过了,而且解释得很好。非常感谢。
  • 现在,两个人来自同一个地方Atlanta,在同一个行业(会计/ADP)工作并计划悬赏以获取已回答many 次的答案前? :)
  • @torox。没有关系……真的!我在亚特兰大住了 7 年,直到我们的孩子分别上大学和职业学校。现在我们回到了阳光明媚的佛罗里达州中部。 :-) 干杯!
  • 尽管曲线下方的填充看起来很相似,但也增加了复杂性。而且我从来没有见过马克,我也有两个星期没有为 adp 工作了。我现在在职业建设者。需要更新我的个人资料。
  • 你是如何让曲线如此完美匹配的?有没有工具可以帮助进行绘图?另外线条有点锯齿,如何提高分辨率使其更平滑?
【解决方案2】:

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

线性版本只是为了感兴趣

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

如您所见,它只是一条线。二次由线性插值3(线)组成,三次为6。

对于这个问题,15% 的性能提升是微不足道和无关紧要的,但对于更密集的需求,15% 的性能提升是非常值得的几行额外的代码,更不用说它看起来更好了。哈哈

【讨论】:

  • @Blindman67。哈哈!你说得对!我是一名从事“日常工作”的会计师,所以是的,我倾向于(过度?)关注精确度——当银行报告他们的账户中有“大约”一百美元时,拥有 107.35 美元的人会皱眉头!我喜欢你对传统三次和二次区间的更快近似。您介意我将您的代码添加到我的“工具包”吗?恕我直言,canvas 应该实现类似于 SVG 的 getPointAtLength 的东西,所以我们不必重新填充该功能。干杯!
  • @markE 如果我不想让每个人都拿走他们需要的东西,我不会发帖。让我感到欣慰的是为定义 SVG 所做的所有持续努力,W3C 起草了 SVG2 而对画布却一无所获。帆布无处不在,它杀死了flash,几乎所有的广告都是基于帆布的。您确实必须寻找 SVG,而当您看到它时,只有静态图像,这确实是一种资源浪费。画布非常需要一些小调整才能真正解锁 GPU,以及一些本机功能,即getPointAtLength,但 W3C 说,一切都完成了!当纯种马刚刚热身时,他们为什么要鞭打那匹死马(SVG)。
  • @Blindman67。是的,我同意 SVG 得到了 W3C 的大部分关注。太糟糕了,因为画布是一个不错的选择……我认为它是 Flash + Photoshop——现在这是一个很棒的组合!而且(!),当 Path2D 是跨浏览器兼容时,我们可以获取所有这些 SVG 数据路径并将它们直接绘制到画布上。话虽如此(作为一名会计师),D3(基于 SVG)是一个令人惊叹的数据可视化工具,我离不开它。 :-)
  • @markE 我来自另一个方向,游戏,模拟,并且多年来一直远离商业应用程序。很容易忘记商业世界的数据驱动需求。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-29
相关资源
最近更新 更多