Как связать CSS анимации с помощью Promise? - PullRequest
1 голос
/ 14 марта 2020

Я пытаюсь выяснить, как работает 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();
});

Ответы [ 4 ]

3 голосов
/ 14 марта 2020

В отличие от опубликованных ответов, я бы не рекомендовал использовать window.setTimeout, поскольку это не гарантирует, что таймеры синхронизируются c с конечными событиями анимации, которые иногда выгружаются в графический процессор. Если вы хотите быть более уверенным, , вам следует прослушать событие animationend и разрешить его в самом обратном вызове, то есть:

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

Более того, поскольку вы пишете несколько дублированных логик c для обоих блоков, вы можете абстрагировать все это в обобщенную c функцию, которая возвращает обещание:

// 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 функция немного запутанная. Если вы хотите запускать отдельные анимации перемещения ящиков асинхронно, объявите их как асинхронный метод c и просто ждите вызовов отдельных функций перемещения ящиков, например:

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

См. Пример проверки концепции ( или вы можете видеть это из этого JSFiddle, который я разветвил из вашего исходного):

// 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>
0 голосов
/ 14 марта 2020

Обещание не означает, что оно будет асинхронным. Поскольку разрешение является мгновенным, то, скорее всего, они запускаются одновременно. Если вы хотите задержать, вы можете создать задержку вызова, используя 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()
})()
0 голосов
/ 14 марта 2020

Вы можете разрешить обещание только тогда, когда узнаете, что соответствующее анимационное событие завершено.

Вы можете использовать для этого событие animationend, которое более подходит, чем использование setTimeout с ожидаемой продолжительностью.

Также будет полезно создать одну служебную функцию для создания обещания для этого, а затем повторно использовать ее для любого элемента и любого описания анимации:

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>
0 голосов
/ 14 марта 2020

Вам нужно дождаться завершения анимации, вот рабочий код,

    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/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...