4

我想在我的网站中实现一个动画:

  • 需要更新多个 DOM 元素。
  • 每个 DOM 元素都有自己的动画路径(取决于它们的位置)
  • 并且仍有缓和作用。

如果我为每个元素调用 jQuery 的 animate() 函数(使用 queue: false),它将使每个元素与其余元素稍微不同步。有道理,因为有多个计时器正在运行。

我可以只有一个计时器事件,每个动画步骤都有一个回调吗?就像是:

jQuery.fx.timeline( from, to, easing, function( step, total ) {

      var percentage = step / total;
      // ...update DOM elements based on the percentage of the animation

} );
4

4 回答 4

4

JavaScript 中的所有计时器都基于原生普通的老式 JavaScript 函数setInterval()setTimeout(). 甚至 jQuery 在内部也使用它。

同步定时器的诀窍是确保只有一个setInterval()被调用,所以我们自己构建一些东西。

可以使用以下方式设计动画:

  • 有 10 步,每步间隔 30ms。
  • 整个动画需要 300 毫秒。
  • 在每一步,可以计算当前进度/百分比:

    var 百分比 = ( currentStep / totalSteps );

现在,每次调用你的函数时setInterval(),你可以一次将所有 DOM 元素设置到正确的位置。要找出元素在每个动画帧中的位置,请使用:

var diff       = ( to - from );
var stepValue  = from + diff * percentage;

jQuery的缓动函数可以直接调用,最终语句变为:

var stepValue  = jQuery.easing[ easingMethod ]( percentage, 0, from, diff );

我已经把它变成了一门课:

/**
 * Animation timeline, with a callback.
 */
function AnimationTimeline( params, onstep )
{
  // Copy values.

  // Required:
  this.from     = params.from || 0;         // e.g. 0%
  this.to       = params.to   || 1;         // e.g. 100%
  this.onstep   = onstep || params.onstep;  // pass the callback.

  // Optional
  this.steps    = params.steps    || 10;
  this.duration = params.duration || 300;
  this.easing   = params.easing   || "linear";

  // Internal
  this._diff  = 0;
  this._step  = 1;
  this._timer = 0;
}

jQuery.extend( AnimationTimeline.prototype, {

  start: function()
  {
    if( this.from == this.to )
      return;

    if( this._timer > 0 )
    {
      self.console && console.error("DOUBLE START!");
      return;
    }

    var myself  = this;    
    this._diff  = ( this.to - this.from );
    this._timer = setInterval( function() { myself.doStep() }, this.duration / this.steps );
  }

, stop: function()
  {
    clearInterval( this._timer );
    this._timer = -1;
    this._queue = [];
  }

, doStep: function()
  {
    // jQuery version of: stepValue = from + diff * percentage;
    var percentage = ( this._step / this.steps );
    var stepValue  = jQuery.easing[ this.easing ]( percentage, 0, this.from, this._diff );

    // Next step
    var props = { animationId: this._timer + 10
                , percentage: percentage
                , from: this.from, to: this.to
                , step: this._step, steps: this.steps
                };
    if( ++this._step > this.steps )
    {
      stepValue = this.to;  // avoid rounding errors.
      this.stop();
    }

    // Callback
    if( this.onstep( stepValue, props ) === false ) {
      this.stop();
    }
  }
});

现在您可以使用:

var el1 = $("#element1");
var el2 = $("#element2");

var animation = new AnimationTimeline( {
    easing: "swing"
  , onstep: function( stepValue, animprops )
    {
      // This is called for every animation frame. Set the elements:
      el1.css( { left: ..., top: ... } );
      el2.css( { left: ..., top: ... } );
    }
  });

// And start it.
animation.start();

添加暂停/恢复是读者的练习。

于 2009-12-04T10:27:26.447 回答
4

看看这篇文章:http: //james.padolsey.com/javascript/fun-with-jquerys-animate/

很清楚,很容易!

于 2009-09-24T10:26:29.677 回答
4

一个简单的答案。也许有人会像我一样搜索它。

我们可以在动画函数中使用“step”属性。

$(obj).animate(
    {
        left: 0
    },
    {
        duration: 50,
        step: function( currentLeft ){
            console.log( "Left: ", currentLeft );
        }
    }
 );

所以对于每一步,这将记录当前的左侧位置

于 2013-04-04T19:10:01.210 回答
1

did you check jquery queue?

You can queue your animations and set a callback on each of them, I think if you play little bit with it you can achieve what you want.

Hope it helps, Sinan.

于 2009-09-01T12:42:42.287 回答