首页 > 解决方案 > 避免 JS 中的顺序动画回调地狱

问题描述

我有一个网络项目,我想为五个彩色 div 的不透明度设置动画,使它们依次“闪烁”,然后用户以相同的顺序单击它们(就像 Simon 说的那样)。演示序列在用户单击按钮时开始,并且按钮也会淡出,因此只能单击一次。我的代码是这样的(仅用于演示动画,目前不关心用户响应):

function circleBlink(elem, callback) {
  elem.animate({'opacity':'0'}, function() {
    elem.animate({'opacity':'1'}, function() {
      if (callback && typeof callback === 'function') {
        callback();
      }
    });
  });
}

function runThrough() {
  circleBlink($('.sequence-options > .red-orange'), function() {
    circleBlink($('.sequence-options > .blue'), function() {
      circleBlink($('.sequence-options > .yellow'), function() {
        circleBlink($('.sequence-options > .green'), function() {
          circleBlink($('.sequence-options > .purple'));
        });
      });
    });
  });
}

$('.start-btn').click(function() {
  $that = $(this);
  $that.animate({'opacity': '0'}, function() {
    $that.addClass('hidden');
  });
  runThrough();
  setTimeout(runThrough, 5000);

});

该代码按原样工作正常,但我想知道是否有一种不那么冗长/性能更高/最佳实践的方式来重构它。我正在使用 jQuery,但不想为这个特定项目引入任何其他动画库或插件

标签: javascriptjquery

解决方案


你可以为它创建一个animate返回 Promise 的包装函数,也可以circleBlink变成一个返回 Promise 的函数。您还可以使用箭头函数来避免丑陋that = this

const animateWithOpacity = (jqElm, opacity) => new Promise(resolve => {
  jqElm.animate({ opacity }, resolve);
});
async function circleBlink(elem) {
  await animateWithOpacity(elem, '0');
  await animateWithOpacity(elem, '1');
  // async function will automatically return promise that resolves when end is reached
}

async function runThrough() {
  const classes = ['red-orange', 'blue', 'yellow', 'green', 'purple'];
  for (const className of classes) {
    await circleBlink($('.sequence-options > .' + className));
  }
}

$('.start-btn').click(function() {
  animateWithOpacity($(this), 0)
    .then(() => $(this).addClass('hidden'));
  runThrough();
  setTimeout(runThrough, 5000);
  // might also be able to `runThrough().then(runThrough)` if the timing is right
});

推荐阅读