HTML5画布 - 沿路径动画对象

18

我对canvas等技术有点陌生,所以如果问题很简单请原谅。

我想要能够按照贝塞尔路径(bezier path)定义的路径来动画化一个对象,但我不知道该如何实现。

我查看了Raphael库,但我无法弄清如何随着时间推移按照路径运动对象。

Cake JS在演示中看起来很有希望,但我真的很难理解文档,或者说缺乏文档。

有人有这方面的工作示例吗?

3个回答

21
使用来自这个相关问题的代码在我的网站上,但是在回调函数中不要更改.style.left等内容,而是用新位置(和可选旋转)抹掉并重新绘制您的画布。

请注意,这在内部使用SVG轻松插值bézier曲线上的点,但您可以将其提供的点用于任何您想要的目的(包括在画布上绘制)。

如果我的网站无法访问,这里是库的当前快照:

function CurveAnimator(from,to,c1,c2){
  this.path = document.createElementNS('http://www.w3.org/2000/svg','path');
  if (!c1) c1 = from;
  if (!c2) c2 = to;
  this.path.setAttribute('d','M'+from.join(',')+'C'+c1.join(',')+' '+c2.join(',')+' '+to.join(','));
  this.updatePath();
  CurveAnimator.lastCreated = this;
}
CurveAnimator.prototype.animate = function(duration,callback,delay){
  var curveAnim = this;
  // TODO: Use requestAnimationFrame if a delay isn't passed
  if (!delay) delay = 1/40;
  clearInterval(curveAnim.animTimer);
  var startTime = new Date;
  curveAnim.animTimer = setInterval(function(){
    var now = new Date;
    var elapsed = (now-startTime)/1000;
    var percent = elapsed/duration;
    if (percent>=1){
      percent = 1;
      clearInterval(curveAnim.animTimer);
    }
    var p1 = curveAnim.pointAt(percent-0.01),
        p2 = curveAnim.pointAt(percent+0.01);
    callback(curveAnim.pointAt(percent),Math.atan2(p2.y-p1.y,p2.x-p1.x)*180/Math.PI);
  },delay*1000);
};
CurveAnimator.prototype.stop = function(){
  clearInterval(this.animTimer);
};
CurveAnimator.prototype.pointAt = function(percent){
  return this.path.getPointAtLength(this.len*percent);
};
CurveAnimator.prototype.updatePath = function(){
  this.len = this.path.getTotalLength();
};
CurveAnimator.prototype.setStart = function(x,y){
  var M = this.path.pathSegList.getItem(0);
  M.x = x; M.y = y;
  this.updatePath();
  return this;
};
CurveAnimator.prototype.setEnd = function(x,y){
  var C = this.path.pathSegList.getItem(1);
  C.x = x; C.y = y;
  this.updatePath();
  return this;
};
CurveAnimator.prototype.setStartDirection = function(x,y){
  var C = this.path.pathSegList.getItem(1);
  C.x1 = x; C.y1 = y;
  this.updatePath();
  return this;
};
CurveAnimator.prototype.setEndDirection = function(x,y){
  var C = this.path.pathSegList.getItem(1);
  C.x2 = x; C.y2 = y;
  this.updatePath();
  return this;
};

…这是如何使用它的示例:

var ctx = document.querySelector('canvas').getContext('2d');
ctx.fillStyle = 'red';

var curve = new CurveAnimator([50, 300], [350, 300], [445, 39], [1, 106]);

curve.animate(5, function(point, angle) {
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.fillRect(point.x-10, point.y-10, 20, 20);
});​

在操作中:http://jsfiddle.net/Z2YSt/

可以与任何SVG路径一起使用的代码...只需要更改this.path.setAttribute以使用给定的路径进行初始化。 - Ben
@Ben 当然可以。 :) 你可能会对我这个相关页面感兴趣。 - Phrogz
@Phrogz。Gavin,我试图通过电子邮件联系您,询问您的代码许可证问题,但是它一直被退回并显示未经授权的中继。您是否有其他可以联系到您的地址? - Ben
@Ben 我的名字 at 相同的域名 - Phrogz
我对数学也不是很了解;但你可以通过谷歌搜索并意识到,如果你正在使用Canvas,针对如此简单的问题,这种解决方案是荒谬的。请检查下面我的答案中的数学部分。 - Ivan Castellanos

8

以下是详细的版本:

t 是介于0和1之间的任意数字,代表时间p0p1p2p3对象分别代表起始点第一个控制点第二个控制点终止点

var at = 1 - t;
var green1x = p0.x * t + p1.x * at;
var green1y = p0.y * t + p1.y * at;
var green2x = p1.x * t + p2.x * at;
var green2y = p1.y * t + p2.y * at;
var green3x = p2.x * t + p3.x * at;
var green3y = p2.y * t + p3.y * at;
var blue1x = green1x * t + green2x * at;
var blue1y = green1y * t + green2y * at;
var blue2x = green2x * t + green3x * at;
var blue2y = green2y * t + green3y * at;
var finalx = blue1x * t + blue2x * at;
var finaly = blue1y * t + blue2y * at;

这里是一个使用<canvas>在JSfiddle上沿着路径运动的球:http://jsfiddle.net/JAChJ/3/

变量的名称来自于这个gif图,它是关于Bezier曲线的最好解释:http://en.wikipedia.org/wiki/File:Bezier_3_big.gif

以下是一段简短的代码,可供复制/粘贴使用:

var calcBezierPoint = function (t, p0, p1, p2, p3) {
    var data = [p0, p1, p2, p3];
    var at = 1 - t;
    for (var i = 1; i < data.length; i++) {
        for (var k = 0; k < data.length - i; k++) {
            data[k] = {
                x: data[k].x * at + data[k + 1].x * t,
                y: data[k].y * at + data[k + 1].y * t
            };
        }
    }
    return data[0];
};



相关内容:


1
感谢您的贡献,虽然有些晚了。但是没有必要这么傲慢。@phrogz的答案非常棒,并且适用于任何路径。 - Ben
非常远离令人惊叹的水平;它使用专有库,由单个人编写了太多代码,并且使用SVG,速度较慢,在Android和其他设备上不受支持。而且这也适用于任何路径;只需更改p0、p1、p2和p3的值即可。如果您需要帮助获取正确的数字,可以使用“相关内容”中的第一个链接。 - Ivan Castellanos
我们需要免费提供帮助,而且在这个过程中要保持谦虚。告诉我我们需要做什么。 - Ivan Castellanos
非常感谢您提供的这个很好的解决方案和示例!它对我帮助很大。 - Maximilian Lindsey
如果你把第一句话改成“这里是另一种答案”,我会给你的解决方案点个赞。 - martin jakubik
这个函数很简洁,来自维基百科的gif非常有帮助! - forresto

1
我不会在这种情况下使用Canvas,除非你真的必须使用它。SVG内置了沿路径动画。Canvas需要相当多的数学计算才能使其工作。
这里是一个SVG沿路径动画的例子:这里
这里有一些关于Raphael的讨论:使用Raphael进行SVG路径动画 请注意,Raphael使用的是SVG而不是HTML5 Canvas。
在Canvas中沿着贝塞尔曲线进行动画的一种方法是不断地平分贝塞尔曲线,记录中点,直到您有了许多点(例如每个曲线50个点),您可以沿着该点列表使对象动画化。搜索平分贝塞尔曲线和类似查询以获取相关数学知识。

感谢您的输入。我发现大多数SVG动画示例看起来有些笨拙。 - Ben

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