如何在JavaScript轮盘中平滑控制动画速度?

5
我正在尝试创建一个轮盘/CSGO类型的轮子,我已经拼凑了一些在网络上找到的解决方案。然而,我似乎无法弄清楚如何处理动画/减速轮,使其看起来尽可能平滑
基本思路是我将传入获胜结果号码,然后轮盘将旋转至最短所需时间(_this.spinTime),然后在此之后"应该"优雅地减速,当然最终会落在正确的数字上。
这是我的代码(我已经对关键区域进行了注释):
window.requestAnimFrame = (function() {
  return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function(callback, element) {
      window.setTimeout(callback, 1000 / 60);
    };
})();

SpinWheel = function(id) {

  var _this = this;

  _this.el = $('.wheel-wrapper');
  _this.speed = 0;
  _this.startTime = new Date();
  _this.items = 20;
  _this.itemsWidth = 50;
  _this.spinTime = 3000;
  _this.running = false;
  _this.motion = 20;
  _this.resultId = id;
  _this.resultOffset = null;

  _this.setup = function() {
    _this.resultOffset = _this.resultId * _this.itemsWidth;
    _this.loop();
  };

  _this.loop = function() {
    _this.running = true;
    (function gameLoop() {
      _this.update();
      //this returns the translateX to 0 once wheel width is met
      if (_this.speed >= (_this.items * _this.itemsWidth + _this.itemsWidth)) {
        _this.speed = 0;
      }
      _this.speed += _this.motion;
      if (_this.running) {
        requestAnimFrame(gameLoop);
      }
    })();
  };

  _this.update = function() {
    var now = new Date();
    _this.el.css({
      'transform': 'translateX(-' + _this.speed + 'px)'
    });
    //the key area!!
    //if the time elapsed it greater than the spin time, start the slowing down
    if (now - _this.startTime > _this.spinTime) {
        //if the x pos == winning pos && the transition speed is at it's slowest
      if (_this.speed == _this.resultOffset && _this.motion == 1) {
      //stop the animation
        _this.running = false;
        //here we increment down the slowing down
      } else if (_this.speed >= _this.resultOffset) {
        if (_this.motion == 2) {
          _this.motion = 1;
        } else if (_this.speed % _this.motion == 0 && _this.motion > 1) {
          _this.motion -= 2;
        }
      }
      return;
    }

  };


  _this.init = function() {
    _this.setup();
  };

  _this.init();

  return _this;

};
//will be passed in: 20 = number of items
var resultId = parseInt(Math.random() * 20);

var wheel = new SpinWheel(resultId);

如果有更理想的解决方案,请不要客气地拆开它。

点这里查看 Fiddle

正如在 Fiddle 中所见,它还算可以用,但是它并不流畅,并且在减速时不一致等等... 因此,非常感谢您的帮助。

提前致谢!

1个回答

4
你需要实现一些摩擦力。
只需将速度乘以所需摩擦力的一小部分即可。
速度 = 速度 * 摩擦力
速度 = 速度 * 0.8
分数越高,摩擦越小,减速越慢。
编辑:在你的情况下,你可能想对你的运动值应用摩擦力,但我不确定是否需要运行你的代码。
编辑2:
好的,我运行了你的代码,我认为这就是你要找的东西? https://jsfiddle.net/ywm3zbc4/7/
_this.update = function() {
    var now = new Date();
    _this.el.css({
      'transform': 'translateX(-' + _this.speed + 'px)'
    });
    if (now - _this.startTime > _this.spinTime) {
      if (_this.speed == _this.resultOffset && _this.motion == 1) {
        _this.running = false;
      } else if (_this.speed >= _this.resultOffset) {
         _this.motion = _this.motion * 0.99;
      }
      return;
}

};

如我在上一个编辑中提到的那样,我已经将摩擦力应用于您的运动。


第三次编辑(来自评论):

做法是获取目标位置,并使用总宽度的模来缓慢移动到该位置。您可以尝试许多不同的动画效果,但我选择了缓出正弦方程。

这种技术可以称为“滑动窗口动画”,可以将其视为一根杆子沿着轨道滚动到特定位置。

新的示例链接: https://jsfiddle.net/ywm3zbc4/10/

以下是核心代码:

// t: current time
// b: start value
// c: change in value
// d: duration
function easeOutSine(t, b, c, d) {
    return c * Math.sin(t/d * (Math.PI/2)) + b;
}

_this.update = function(rafTime) {
  var deltaTime = rafTime - _this.startTime;
  if (deltaTime >= _this.spinTime) {
    _this.running = false;
    return;
  }
  // t = timeFraction
  var t = easeOutSine(deltaTime, 0, 1, _this.spinTime);
  _this.position = Math.round(t * _this.totalDistance);
  var translateX = _this.position % _this.totalWidth;
  console.log('translateX:', translateX, 'position:', _this.position, 'deltaTime:', deltaTime);
  _this.el.css({
    'transform': 'translateX(-' + translateX + 'px)'
  });
};

嗨,感谢您的快速回答,我已经看了一下,是的,它看起来好多了,现在的问题是它没有停在正确的数字上 :| - user7374467
抱歉,我可能删掉了太多内容以传达思想。现在我看到你的resultOffset后,你可以在一定程度上使用插值来实现它。如果您不熟悉插值,那么这个评论不是解释的合适场所。本质上,您有一个目标位置,但您正在尝试使其看起来随机,我明白了。因此,您需要循环几次,通过一些摩擦减速,等待在速度阈值内达到目标位置,然后缓慢地完成剩余部分。 - drkibitz
1
我想这样做应该可以 https://jsfiddle.net/xareyo/ywm3zbc4/8/,因为我不一定需要预定义目标位置(尽管我认为这样会更可靠)。我想我得看看。 - user7374467
你可以这样获取值:(wheelSize * totalLoops) + resultOffset。然后,将总值插值到该值(从0到x)。接着,使用插值后的“总”值的模数来正确翻译轮子。你可以使用各种插值方法,有许多不同的缓动方程式,但都是相同的。你有一个当前值、目标值、到达目标值的总时间以及变化时间来返回插值。 - drkibitz
我会的,但是得等我坐在电脑前。 - drkibitz
显示剩余4条评论

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