【问题标题】:html5 canvas - animating an object following a pathhtml5 画布 - 沿路径为对象设置动画
【发布时间】:2012-02-14 00:43:03
【问题描述】:

我对画布有点陌生,如果这是一个微不足道的问题,请原谅。

我希望能够按照路径(定义为贝塞尔路径)为对象设置动画,但我不知道该怎么做。

我看过拉斐尔,但随着时间的推移,我不知道如何走这条路。

Cake JS 在演示中看起来很有希望,但我真的很苦恼文档,或者在这种情况下缺少文档。

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

【问题讨论】:

    标签: html animation html5-canvas raphael


    【解决方案1】:

    使用来自this related question 的代码on my website,但不要在回调中更改.style.left 等,而是使用新位置的项目擦除并重新绘制您的画布(并且可以选择旋转)。

    请注意,这在内部使用 SVG 沿贝塞尔曲线轻松插入点,但您可以将它提供的点用于您想要的任何东西(包括在画布上绘图)。

    如果我的网站出现故障,这里是图书馆的当前快照:

    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 当然可以。 :) 你可能对this related page of mine 感兴趣。
    • @Phrogz。 Gavin,试图通过电子邮件向您发送有关您的代码许可证的信息! [at] phrogz.net 但它通过未经授权的中继不断反弹。还有其他可以联系您的地址吗?
    • @Ben myfirstname at samedomain
    • 我对数学也不太了解;但是你可以谷歌并意识到这个解决方案对于这样一个简单的问题是荒谬的,特别是如果你使用的是画布。检查下面我的答案中的数学。
    【解决方案2】:

    所以,这里是详细的版本:

    t 是介于 0 和 1 之间的任意数字,代表 时间p0p1p2p3 对象是 起点点、1st control点、2nd control点和end point分别:

    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;
    

    这是ball using <canvas> following a path in JSfiddle

    变量的名称来自这个 gif,这是对贝塞尔曲线的最佳解释: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];
    };
    



    相关内容:

    【讨论】:

    • 感谢您的后期贡献。不过没必要这么嚣张。 @phrogz 的回答非常棒,适用于任何路径。
    • 离awesome很远;它使用了一个专有库,其中一个人的代码太多,它使用的 SVG 速度较慢,并且在 Android 和其他设备上不受支持。 ---- 这也适用于任何路径;只需更改 p0、p1、p2 和 p3 的值。如果您需要帮助以获取正确的数字,可以使用“相关内容”中的第一个链接。
    • 我们需要免费提供帮助,我们需要谦虚吗?告诉我我们需要做的一切。
    • 感谢您提供这个非常好的解决方案和您的示例!对我帮助很大
    • 如果您将第一句话更改为“这是一个替代答案”,我会给您的解决方案 +1。
    【解决方案3】:

    除非你真的必须这样做,否则我不会为此使用 Canvas。 SVG 具有沿内置路径的动画。Canvas 需要大量数学才能使其工作。

    Here's one example of SVG animating along a path.

    这里有一些关于拉斐尔的讨论:SVG animation along path with Raphael

    请注意,Raphael 使用 SVG 而不是 HTML5 Canvas。


    在 Canvas 中沿贝塞尔路径设置动画的一种方法是连续平分贝塞尔曲线,重新计算中点,直到您有很多点(例如,每条曲线 50 个点),您可以沿该点列表为对象设置动画.搜索二等分贝塞尔曲线和相关数学的类似查询。

    【讨论】:

    • 感谢您的意见。我发现的大多数 svg 动画示例看起来...笨重
    猜你喜欢
    • 1970-01-01
    • 2011-09-28
    • 2023-03-25
    • 2013-01-16
    • 2021-07-20
    • 2012-07-14
    • 1970-01-01
    • 2021-05-16
    • 1970-01-01
    相关资源
    最近更新 更多