При выполнении css-перехода на реагирующем элементе вам нужно иметь дело с дополнительным состоянием: самим переходом.
Итак, сначала вы изменяете состояние ваших компонентов, так что переход css изменит их, затем вы изменяете состояние в конце перехода, чтобы заставить реакцию соответствовать стилю компонента в конце перехода.
В случае, когда вы нажимаете, чтобы переключить элемент, вы должны вычислить изменение CSS, которое будет иметь тот же эффект, что и переключение элементов, а затем, когда переход завершится, вы фактически переключите элементы.
Нам потребуется добавить свойство top к элементам и соответствующий ключ:
constructor() {
super();
this.state = {
items: [
{ key: 0, name: "Hello", top: 0 },
{ key: 1, name: "Sello", top: 0 }, // move this down
{ key: 2, name: "Wello", top: 0 }, // move this up
{ key: 3, name: "Zello", top: 0 },
{ key: 4, name: "Pello", top: 0 },
{ key: 5, name: "Aello", top: 0 }
],
transitioning: {}
};
this.itemTransitionCount = 0;
}
Обратите внимание на itemTransitionCount для дальнейшего использования.
Сначала оберните элементы <li>
в элемент <ul>
и прикрепите ссылку к <ul>
:
ulLoad = c => (this.ulDom = c);
render() {
...
<ul
ref={this.ulLoad}
style={{ position: "relative", display: "inline-block" }}
>
{
this.state.items.map(item => (
<li
key={item.key}
onTransitionEnd={this.itemTransitionEnd}
style={{
transition: item.top ? "top 0.5s ease" : "none",
position: "relative",
top: item.top
}}
>
{item.name}
</li>
))
}
</ul>
....
}
Это позволит вычислить относительное положение детей <li>
с
Затем измените ваш обработчик перестановки следующим образом:
reShuffle = () => {
this.setState({
items: this.state.items.map((item, index) => {
if (index === 1) {
const top2 = this.ulDom.children[2].offsetTop;
const top1 = this.ulDom.children[1].offsetTop;
const top = top2 - top1;
return { ...this.state.items[1], top };
}
if (index === 2) {
const top1 = this.ulDom.children[1].offsetTop;
const top2 = this.ulDom.children[2].offsetTop;
const top = top1 - top2;
return { ...this.state.items[2], top };
}
return item;
})
});
Этот код будет инициировать верхний переход, который мы хотим установить для элементов li => анимация переключения будет происходить во время следующего рендеринга.
Мы завершим обе анимации с помощью обработчика onTransitionEnd (проверка на evt.target! == evt.currentTarget есть, потому что пузырьки событий перехода присутствуют, потому что оба элемента вызовут событие, но мы хотим, чтобы действовать один раз в конце обоих переходов):
itemTransitionEnd = evt => {
if (evt.target !== evt.currentTarget) return;
// alert("transitionEnd");
this.itemTransitionCount++;
if (this.itemTransitionCount < 2) return;
this.itemTransitionCount = 0;
this.setState({
items: this.state.items.map((item, index) => {
if (index === 1) {
return { ...this.state.items[2], top: 0 };
}
if (index === 2) {
return { ...this.state.items[1], top: 0 };
}
return item;
})
});
};
Приведенный выше код фактически переключает элемент в конце перехода и сбрасывает их относительную вершину (счетчик переходов также сбрасывается для следующего перехода).
Это иллюстрирует двухэтапный рендеринг для анимации перехода CSS в React.
Рабочая песочница здесь