【问题标题】:How to chain CSS animations using Promise?如何使用 Promise 链接 CSS 动画?
【发布时间】:2020-03-14 14:00:13
【问题描述】:

我试图弄清楚Promise 的工作原理,并让一个 CSS 动画一个接一个地运行。但是它们同时运行...

JS fiddle

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();
});

【问题讨论】:

  • 您正在正确地将您的承诺返回函数调用链接在一起。但是这些函数会立即解决它们返回的承诺,而无需等待动画完成!
  • Promise 不会使任何事情异步。它们用于管理其他 API 中的异步行为。

标签: javascript ecmascript-6 css-animations es6-promise


【解决方案1】:

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

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';
  });
}

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

// 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');
}

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

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

查看概念验证示例(或者您可以从原始示例中的 this JSFiddle that I've forked 查看它):

// 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');
}

let move_boxes = async () => {
  await move_box_one();
  await move_box_two();
}
move_boxes().then(() => console.log('boxes moved'));
#div_one {
  width: 50px;
  height: 50px;
  border: 10px solid red;
}

#div_two {
  width: 50px;
  height: 50px;
  border: 10px solid blue;
}

@keyframes move {
  100% {
    transform: translateX(300px);
  }
}

@keyframes down {
  100% {
    transform: translateY(300px);
  }
}
<div id="div_one"></div>
<div id="div_two"></div>

【讨论】:

【解决方案2】:

当您知道相应的动画事件已完成时,您将只想解决承诺。

您可以为此使用 animationend 事件,这比使用 setTimeout 和预期持续时间更合适。

创建一个实用函数来为此创建一个promise,然后为任何元素和任何动画描述重用它也会很有用:

const promiseAnimation = (elem, animation) => new Promise(resolve => {
    elem.style.animation = animation;
    elem.addEventListener("animationend", resolve);
});

const $ = document.querySelector.bind(document);

const moveBoxOne = () => promiseAnimation($("#div_one"), "move 3s forwards");
const moveBoxTwo = () => promiseAnimation($("#div_two"), "move 3s forwards");


Promise.resolve()
    .then(moveBoxOne)
    .then(() => console.log("one has moved"))
    .then(moveBoxTwo)
    .then(() => console.log("two has moved"))
div { display: inline-block; position: relative; border: 1px solid; }
@keyframes move {
  from {margin-left: 0px;}
  to {margin-left: 100px;}
}
<div id="div_one">div_one</div><br>
<div id="div_two">div_two</div>

【讨论】:

    【解决方案3】:

    您需要等待动画完成,这是一个工作代码,

        let move_boxes = () => {
        return new Promise( (resolve, reject) => {
            resolve ('boxes moved!')
        })
    };
    let move_box_one = () => {
        return new Promise( (resolve, reject) => {
            document.getElementById('div_two').style.animation = 'move 3s forwards';
            setTimeout(()=> resolve(), 3000);   
        })
    
    }
    let move_box_two = () => {
        return new Promise( (resolve, reject) => {
            document.getElementById('div_one').style.animation = 'move 3s forwards';
            setTimeout(()=> resolve(), 3000);
        })
    }
    
    move_boxes().then(() => {
        return move_box_one();
    }).then(() => {
        return move_box_two();
    });
    

    工作小提琴:https://jsfiddle.net/bu96cxak/

    【讨论】:

      【解决方案4】:

      Promise 并不意味着它将是异步的。由于解析是即时的,因此它们很可能同时运行。如果您想延迟,您可以使用 setTimeout 在通话中创建延迟。

      const delay = (dl) => new Promise(r => {
          setTimeout(r, dl)
      })
      
      let move_box_one = () => {
          document.getElementById('div_two').style.animation = 'move 3s forwards'
          console.log('box one moved!') 
      
      }
      let move_box_two = () => {
          document.getElementById('div_one').style.animation = 'move 3s forwards'
          console.log('box two moved!')       
      }
      
      (async function() {
          move_box_one()
          await delay(200)
          move_box_two()
      })()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-07
        • 1970-01-01
        • 2016-08-05
        相关资源
        最近更新 更多