html5 canvas - animating an object following a path

前端 未结 3 945
情话喂你
情话喂你 2021-01-31 05:12

I\'m a bit new to canvas and such so forgive if it\'s a trivial question.

I\'d like to be able to animate an object following a path (defined as bezier path) but I\'m no

相关标签:
3条回答
  • 2021-01-31 05:56

    Use the code on my website from this related question, but instead of changing the .style.left and such in the callback, erase and re-draw your canvas with the item at the new location (and optionally rotation).

    Note that this uses SVG internally to easily interpolate points along a bézier curve, but you can use the points it gives you for whatever you want (including drawing on a Canvas).

    In case my site is down, here's a current snapshot of the library:

    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;
    };
    

    …and here's how you might use it:

    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);
    });​
    

    In action: http://jsfiddle.net/Z2YSt/

    0 讨论(0)
  • 2021-01-31 06:09

    I wouldn't use Canvas for this unless you really have to. SVG has animation along a path built in. Canvas requires quite a bit of math to get it working.

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

    Here's some discussion about it for raphael: SVG animation along path with Raphael

    Please note that Raphael uses SVG and not HTML5 Canvas.


    One way to animate along a bezier path in Canvas is to continuously bisect the bezier curve, recoring the midpoints until you have a lot of points (say, 50 points per curve) that you can animate the object along that list of points. Search for bisecting beziers and similar queries for the related math on that.

    0 讨论(0)
  • 2021-01-31 06:16

    So, here is the verbose version:

    t being any number between 0 and 1 representing time; the p0, p1, p2, p3 objects are the start point, the 1st control point, the 2nd control point an the end point respectively:

    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;
    

    Here is a ball using <canvas> following a path in JSfiddle

    The names of the variables come from this gif wich is the best explication for bezier curves: http://en.wikipedia.org/wiki/File:Bezier_3_big.gif

    A short version of the code, inside a function ready to copy/paste:

    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];
    };
    



    Related stuff:

    • http://blogs.sitepointstatic.com/examples/tech/canvas-curves/bezier-curve.html
    • http://13thparallel.com/archive/bezier-curves/
    • http://gsgd.co.uk/sandbox/jquery/easing/jquery.easing.1.3.js
    • http://www.youtube.com/watch?v=hUCT4b4wa-8
    0 讨论(0)
提交回复
热议问题