使用线性插值进行不一致的速度动画制作

时间:2013-07-11 05:06:01

标签: javascript html5 math

我正在尝试在两个计算点之间为画布周围的对象设置动画。但是,我使用的方法似乎没有考虑点之间的距离。例如,远距离拍摄动画的时间与短距离相同。

以一致的速度制作动画对象的最佳方法是什么?

/**
 * Update function that is called in a setInterval. Moves boid to a new position
 *
 **/
this.update = function(){
    context.clearRect(0,0,canvas.width,canvas.height);


    this.amount += 0.005;
    if (this.amount > 1) this.kill();

    this.x = this.origX + (this.destX - this.origX) * this.amount;
    this.y = this.origY + (this.destY - this.origY) * this.amount;

    this.drawBoid();

    //console.log(this.x + ' ' + this.y);




}

2 个答案:

答案 0 :(得分:3)

您需要采用一种方法,根据自上一帧以来已用时间以及您希望以距离为单位设置动画的速度进行动画处理-per单位时间。

计算经过的时间时你必须非常小心;只是因为你安排setIntervaln毫秒启动一次,并不意味着你的代码会在那个时候发射。更糟糕的是,setInterval最小延迟为4毫秒,无论如何。 Really!而是在代码运行时依赖于时钟

更好的是,现代浏览器有一个名为requestAnimationFrame()的方法,每当重绘即将发生时,它会调用一大块代码。你传递一个回调函数,并以时间戳作为第一个参数调用该方法。

// Find your browser's implementation of requestAnimationFrame
window.requestAnimationFrame =
  window.requestAnimationFrame || 
  window.mozRequestAnimationFrame ||
  window.webkitRequestAnimationFrame || 
  window.msRequestAnimationFrame;

// The update method
var update = function(timestamp) {
  context.clearRect(0, 0, canvas.width, canvas.height);

  // How much time has elapsed since the start of the animation?
  var elapsedTime = timestamp - startTime;

  // How far have we moved at that time?
  var distanceTravelled = elapsedTime * speed;
  if (distanceTravelled >= totalDistance) distanceTravelled = totalDistance; // Don't overshoot

  // How far have we moved in each component?
  var distanceTravelledX = xPerUnitDistance * distanceTravelled;
  var distanceTravelledY = yPerUnitDistance * distanceTravelled;

  // Move there!
  this.x = Math.round(origin.x + distanceTravelledX);
  this.y = Math.round(origin.y + distanceTravelledY);

  // Draw!
  this.drawBoid();

  if (distanceTravelled < totalDistance) {
    // Schedule update to be called just before the next repaint
    requestAnimationFrame(update);
  }
}

// The distance you want to move
var distance = 1; // In distance units

// Speed you want to move at
var speed = 0.005 / 1000; // In distance units per millisecond

// Storage for the time when your animation started
var startTime;

// The start point, in distance units
var origin = {
  x: 0,
  y: 0
};

// The destination, in distance units
var destination = {
  x: 100,
  y: 75
};

// Distance to travel
var deltaX = (destination.x - origin.x);
var deltaY = (destination.y - origin.y);
var totalDistance = Math.sqrt( Math.pow(deltaX, 2) + Math.pow(deltaY, 2) );

// Storage for the contribution of each component per unit distance
var xPerUnitDistance,
    yPerUnitDistance;

if (totalDistance > 0) { 
  // Start animating!
  xPerUnitDistance = deltaX / totalDistance;
  yPerUnitDistance = deltaY / totalDistance;

  // Get the start time
  startTime = window.performance.now ?
              // Some browsers use high-precision timers
              (performance.now() + performance.timing.navigationStart) : 
              Date.now(); // A fallback for those that don't

  update(startTime);
}

更新Adam指出Chrome uses a high precision timer。代码已更新。

答案 1 :(得分:0)

这是基本的运动学,答案取决于你想要的运动类型。

如果你想要一个恒定的速度,那么你会想要像这样的伪代码:

function moveConstantVelocity(curPos, targetPos, speed, t)
{
    /*
     * curPos - Current position of object (curPos.x, curPos.y)
     * targetPos - Destination position (targetPos.x, targetPos.y)
     * speed - Pixels-per-second to move
     * t - Seconds elapsed since previous move command
     */
    delta.x = targetPos.x - curPos.x;
    delta.y = targetPos.y - curPos.y;
    distance = sqrt(delta.x*delta.x + delta.y*delta.y);

    if (speed*t > distance)
    {
        // don't overshoot target
        newPos.x = targetPos.x;
        newPos.y = targetPos.y;
    }
    else
    {
        // Compute movement vector by normalizing delta vector
        // and then scaling it by distance traveled
        movement.x = (delta.x/distance)*speed*t;
        movement.y = (delta.y/distance)*speed*t;

        // apply movement
        newPos.x = origPos.x + movement.x;
        newPos.y = origPos.y + movement.y;
    }

    return newPos;
}