如何使用Canvas动画绘制曲线?

5

我有一堆点需要慢慢绘制。我尝试使用 教程 中的setTimeOut函数来实现,但效果并不好。

这个函数的样子是这样的:

函数:

var myFunction = function(ctx, grid, points) {
                ctx.beginPath();
                ctx.moveTo(points[0].x, points[0].y);
                ctx.lineWidth = 2;
                ctx.strokeStyle = '#2068A8';
                ctx.fillStyle = '#2068A8';
                var count = 1;
                for (count = 1; count < points.length; count++) {
                    ctx.lineTo(points[count].x , points[count].y);
                }
                ctx.stroke();
            }

在这个函数周围有许多其他的绘图函数,但我只想动画显示一个。

如何使用canvas慢慢地绘制一个函数?


你能否提供一下你目前所做的演示:http://jsfiddle.net/ - Aadit M Shah
嗯!我希望我能够……想象一下在图表上有两条线和一条需要用几个点绘制的线。我只是想通过几个点来实现动画效果。谢谢。 - 3logy
2个回答

10

我能想到两种方法来完成这个任务。一种是在画出第一个点后暂停一段时间,再画出另一个点。这就是我提供的第一个示例。第二种方法涉及将部分线条绘制到当前目标位置,这样会使绘图效果更加平滑。顺便说一下,在这两个示例中我都使用了requestAnimationFrame,它是推荐用于任何类型的canvas动画的方式。

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d");

canvas.width = 400;
canvas.height = 200;

var points = [],
    currentPoint = 1,
    nextTime = new Date().getTime()+500,
    pace = 200;

// make some points
for (var i = 0; i < 50; i++) {
    points.push({
        x: i * (canvas.width/50),
        y: 100+Math.sin(i) * 10
    });
}

function draw() {

    if(new Date().getTime() > nextTime){
        nextTime = new Date().getTime() + pace;

        currentPoint++;
        if(currentPoint > points.length){
            currentPoint = 0;
        }
    }
    ctx.clearRect(0,0,canvas.width, canvas.height);
    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);
    ctx.lineWidth = 2;
    ctx.strokeStyle = '#2068A8';
    ctx.fillStyle = '#2068A8';
    for (var p = 1, plen = currentPoint; p < plen; p++) {
        ctx.lineTo(points[p].x, points[p].y);
    }
    ctx.stroke();

    requestAnimFrame(draw);
}

draw();

演示实例

如果您发现画出的线条有些粗糙,您可以按照以下步骤使其更加平滑。

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d");

canvas.width = 400;
canvas.height = 200;

var points = [],
    currentPoint = 1,
    speed = 2,
    targetX = 0,
    targetY = 0,
    x = 0,
    y = 0;

// make some points
for (var i = 0; i < 50; i++) {
    points.push({
        x: i * (canvas.width/50),
        y: 100+Math.sin(i) * 10
    });
}

// set the initial target and starting point
targetX = points[1].x;
targetY = points[1].y;
x = points[0].x;
y = points[0].y;

function draw() {
    var tx = targetX - x,
        ty = targetY - y,
        dist = Math.sqrt(tx*tx+ty*ty),
        velX = (tx/dist)*speed,
        velY = (ty/dist)*speed;

        x += velX
        y += velY;

    if(dist < 1){
        currentPoint++;

        if(currentPoint >= points.length){
            currentPoint = 1;
            x = points[0].x;
            y = points[0].y;
        }

        targetX = points[currentPoint].x;
        targetY = points[currentPoint].y;
    }

    ctx.clearRect(0,0,canvas.width, canvas.height);
    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);
    ctx.lineWidth = 2;
    ctx.strokeStyle = '#2068A8';
    ctx.fillStyle = '#2068A8';

    for (var p = 0, plen = currentPoint-1; p < plen; p++) {
        ctx.lineTo(points[p].x, points[p].y);
    }
    ctx.lineTo(x, y);    
    ctx.stroke();

    requestAnimFrame(draw);
}

draw();

实时演示


1
太棒了!干得好,Loktar!! :) - 3logy
但是有一个问题,如何在绘制结束时停止动画? - 3logy
2
我的错,只需删除“currentPoint = 0;”即可。 - 3logy
如何连续绘制? - Karthik
1
我非常喜欢这里实现的效果(http://www.maissan.net/articles/simulating-vines/2),它非常流畅,动画效果也很美。 - Paul Carroll

0

谢谢你的这些例子,Loktar!这应该是对Loktar发布的答案中第二个示例的评论,但我无法进行评论,因为我刚注册并且没有足够的“声誉”。

我尝试了上面的第二个演示文稿,如果你减慢速度,你会注意到新的线段从它应该开始的点之前的地方开始增长。这就是我修改它让它正常工作的方法:

我改变了以下行:

ctx.lineTo(points[p].x, points[p].y); 
// => 
ctx.lineTo(points[p+1].x, points[p+1].y);

现在结果看起来非常流畅。我发表评论是因为Loktar的帖子几乎完美,我希望人们不会只因为需要微调的一个小细节而错过他的帖子。再次感谢,Loktar!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接