如何使用 Promise 链接 CSS 动画?

Dra*_*rax 2 javascript css-animations ecmascript-6 es6-promise

我试图弄清楚如何Promise工作并使一个 CSS 动画一个接一个地运行。但是它们同时运行......

JS小提琴

let move_boxes = () => {
    return new Promise( (resolve, reject) => {
        resolve ('boxes moved!')
    })
};
let move_box_one = () => {
    return new Promise( (resolve, reject) => {
        resolve (document.getElementById('div_two').style.animation = 'move 3s forwards')
        console.log('box one moved!')   
    })

}
let move_box_two = () => {
    return new Promise( (resolve, reject) => {
        resolve (document.getElementById('div_one').style.animation = 'move 3s forwards')
        console.log('box two moved!')       
    })
}

move_boxes().then(() => {
    return move_box_one();
}).then(() => {
    return move_box_two();
});
Run Code Online (Sandbox Code Playgroud)

Ter*_*rry 9

与发布的答案相反,我不鼓励使用window.setTimeout,因为它不能保证计时器与动画结束事件同步,动画结束事件有时会卸载到 GPU。如果您想更加确定,您应该监听animationendevent,并在回调本身中解决它,即:

let move_box_one = () => {
  return new Promise((resolve, reject) => {
    const el = document.getElementById('div_one');
    const onAnimationEndCb = () => {
      el.removeEventListener('animationend', onAnimationEndCb);
      resolve();
    }

    el.addEventListener('animationend', onAnimationEndCb)
    el.style.animation = 'move 3s forwards';
  });
}
Run Code Online (Sandbox Code Playgroud)

更好的是,由于您为两个盒子编写了一些重复的逻辑,您可以将所有这些抽象成一个返回承诺的通用函数:

// We can declare a generic helper method for one-time animationend listening
let onceAnimationEnd = (el, animation) => {
  return new Promise(resolve => {
    const onAnimationEndCb = () => {
      el.removeEventListener('animationend', onAnimationEndCb);
      resolve();
    }
    el.addEventListener('animationend', onAnimationEndCb)
    el.style.animation = animation;
  });
}

let move_box_one = async () => {
  const el = document.getElementById('div_one');
  await onceAnimationEnd(el, 'move 3s forwards');
}
let move_box_two = async () => {
  const el = document.getElementById('div_two');
  await onceAnimationEnd(el, 'move 3s forwards');
}
Run Code Online (Sandbox Code Playgroud)

另外,你的move_boxes函数有点复杂。如果您想异步运行单个盒子移动动画,请将其声明为异步方法并简单地等待单个盒子移动函数调用,即:

let move_boxes = async () => {
  await move_box_one();
  await move_box_two();
}
move_boxes().then(() => console.log('boxes moved'));
Run Code Online (Sandbox Code Playgroud)

请参阅概念验证示例(或者您可以从这个 JSFiddle 中看到它,我从您的原始示例中分叉出来):

let move_box_one = () => {
  return new Promise((resolve, reject) => {
    const el = document.getElementById('div_one');
    const onAnimationEndCb = () => {
      el.removeEventListener('animationend', onAnimationEndCb);
      resolve();
    }

    el.addEventListener('animationend', onAnimationEndCb)
    el.style.animation = 'move 3s forwards';
  });
}
Run Code Online (Sandbox Code Playgroud)
// We can declare a generic helper method for one-time animationend listening
let onceAnimationEnd = (el, animation) => {
  return new Promise(resolve => {
    const onAnimationEndCb = () => {
      el.removeEventListener('animationend', onAnimationEndCb);
      resolve();
    }
    el.addEventListener('animationend', onAnimationEndCb)
    el.style.animation = animation;
  });
}

let move_box_one = async () => {
  const el = document.getElementById('div_one');
  await onceAnimationEnd(el, 'move 3s forwards');
}
let move_box_two = async () => {
  const el = document.getElementById('div_two');
  await onceAnimationEnd(el, 'move 3s forwards');
}
Run Code Online (Sandbox Code Playgroud)
let move_boxes = async () => {
  await move_box_one();
  await move_box_two();
}
move_boxes().then(() => console.log('boxes moved'));
Run Code Online (Sandbox Code Playgroud)