React-анимация монтирования и размонтирования одного компонента
что-то такое простое должно быть легко выполнено, но я вытаскиваю свои волосы из-за того, насколько это сложно.
все, что я хочу сделать, это анимировать монтаж и демонтаж компонента React, вот и все. Вот что я пробовал до сих пор, и почему каждое решение не будет работать:
-
ReactCSSTransitionGroup
- Я вообще не использую классы CSS, это все стили JS, поэтому это не сработает. -
ReactTransitionGroup
- этот API нижнего уровня великолепен, но он требует, чтобы вы использовали обратный вызов когда анимация будет завершена, поэтому просто использование CSS-переходов здесь не будет работать. Всегда есть анимационные библиотеки, что приводит к следующему пункту: - GreenSock-лицензирование слишком ограничительно для использования в бизнесе IMO.
- React Motion-это кажется большим, но
TransitionMotion
чрезвычайно запутанно и слишком сложно для того, что мне нужно. - конечно, я могу просто сделать обман, как делает материал UI, где элементы отображаются, но остаются скрытыми (
left: -10000px
), но Я бы предпочел не идти этим путем. Я считаю, что это хаки, и я хочу мои компоненты для размонтирования, чтобы они очищались и не загромождали DOM.
Я хочу что-то, что легко для реализации. При монтировании анимируйте набор стилей; при размонтировании анимируйте тот же (или другой) набор стилей. Сделанный. Он также должен быть высокопроизводительным на нескольких платформах.
Я ударился о кирпичную стену здесь. Если я что-то упускаю и есть простой способ сделай это, дай мне знать.
6 ответов
это немного долго, но я использовал все собственные события и методы для достижения этой анимации. Нет!--2-->, ReactTransitionGroup
и т. д.
вещи, которые я использовал
- методы жизненного цикла React
-
onTransitionEnd
событие
как это работает
- установите элемент на основе пропеллера крепления (
mounted
) и со стилем по умолчанию (opacity: 0
) - после установки или обновить, использовать
componentDidMount
(componentWillReceiveProps
для дальнейших обновлений), чтобы изменить стиль (opacity: 1
) с таймаутом (чтобы сделать его асинхронным). - во время размонтирования передайте опору компоненту для идентификации размонтирования, снова измените стиль(
opacity: 0
),onTransitionEnd
, удалите размонтировать элемент из DOM.
продолжить цикл.
пройдись по коду, ты поймешь. Если требуется какое-либо разъяснение, просьба оставить комментарий.
надеюсь, что это помогает.
class App extends React.Component{
constructor(props) {
super(props)
this.transitionEnd = this.transitionEnd.bind(this)
this.mountStyle = this.mountStyle.bind(this)
this.unMountStyle = this.unMountStyle.bind(this)
this.state ={ //base css
show: true,
style :{
fontSize: 60,
opacity: 0,
transition: 'all 2s ease',
}
}
}
componentWillReceiveProps(newProps) { //check for the mounted props
if(!newProps.mounted)
return this.unMountStyle() //call outro animation when mounted prop is false
this.setState({ //remount the node when the mounted prop is true
show: true
})
setTimeout(this.mountStyle, 10) //call the into animiation
}
unMountStyle() { //css for unmount animation
this.setState({
style: {
fontSize: 60,
opacity: 0,
transition: 'all 1s ease',
}
})
}
mountStyle() { // css for mount animation
this.setState({
style: {
fontSize: 60,
opacity: 1,
transition: 'all 1s ease',
}
})
}
componentDidMount(){
setTimeout(this.mountStyle, 10) //call the into animiation
}
transitionEnd(){
if(!this.props.mounted){ //remove the node on transition end when the mounted prop is false
this.setState({
show: false
})
}
}
render() {
return this.state.show && <h1 style={this.state.style} onTransitionEnd={this.transitionEnd}>Hello</h1>
}
}
class Parent extends React.Component{
constructor(props){
super(props)
this.buttonClick = this.buttonClick.bind(this)
this.state = {
showChild: true,
}
}
buttonClick(){
this.setState({
showChild: !this.state.showChild
})
}
render(){
return <div>
<App onTransitionEnd={this.transitionEnd} mounted={this.state.showChild}/>
<button onClick={this.buttonClick}>{this.state.showChild ? 'Unmount': 'Mount'}</button>
</div>
}
}
ReactDOM.render(<Parent />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-with-addons.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
используя знания, полученные из ответа Пранеша, я придумал альтернативное решение, которое настраивается и используется повторно:
const AnimatedMount = ({ unmountedStyle, mountedStyle }) => {
return (Wrapped) => class extends Component {
constructor(props) {
super(props);
this.state = {
style: unmountedStyle,
};
}
componentWillEnter(callback) {
this.onTransitionEnd = callback;
setTimeout(() => {
this.setState({
style: mountedStyle,
});
}, 20);
}
componentWillLeave(callback) {
this.onTransitionEnd = callback;
this.setState({
style: unmountedStyle,
});
}
render() {
return <div
style={this.state.style}
onTransitionEnd={this.onTransitionEnd}
>
<Wrapped { ...this.props } />
</div>
}
}
};
использование:
import React, { PureComponent } from 'react';
class Thing extends PureComponent {
render() {
return <div>
Test!
</div>
}
}
export default AnimatedMount({
unmountedStyle: {
opacity: 0,
transform: 'translate3d(-100px, 0, 0)',
transition: 'opacity 250ms ease-out, transform 250ms ease-out',
},
mountedStyle: {
opacity: 1,
transform: 'translate3d(0, 0, 0)',
transition: 'opacity 1.5s ease-out, transform 1.5s ease-out',
},
})(Thing);
и, наконец, в другом компонента render
способ:
return <div>
<ReactTransitionGroup>
<Thing />
</ReactTransitionGroup>
</div>
я поднял эту проблему во время моей работы, и просто, как казалось, это действительно не реагируют. В обычном сценарии, где вы визуализируете что-то вроде:
this.state.show ? {childen} : null;
as this.state.show
изменения дети монтируются / размонтируются сразу же.
один подход я взял это создание компонента фантик Animate
и использовать его как
<Animate show={this.state.show}>
{childen}
</Animate>
теперь this.state.show
изменения, мы можем воспринимать изменения опоры с getDerivedStateFromProps(componentWillReceiveProps)
и создать промежуточные этапы рендеринга для выполнения анимации.
начнем с Статический Этап когда дети установлены или unmounted.
как только мы обнаружить show
флаг меняется, вводим Подготовительный Этап где мы вычисляем необходимые свойства, такие как height
и width
С ReactDOM.findDOMNode.getBoundingClientRect()
.
В конце перехода, мы используем onTransitionEnd
API, чтобы вернуться к
Static
этап.
есть гораздо больше деталей, как этапы передачи плавно, но это может быть общая идея:)
если кто-то заинтересован, я создал библиотеку React https://github.com/MingruiZhang/react-animate-mount поделиться своим решением. Любая обратная связь добро пожаловать:)
Что делать, если onMount вы добавляете другое имя класса, в котором есть переход, и onUnMount вы удаляете это имя класса?
для тех, кто рассматривает react-motion, анимация одного компонента, когда он монтируется и размонтируется, может быть подавляющей для настройки.
есть библиотека под названием react-motion-ui-pack что делает этот процесс намного проще для начала. Это обертка вокруг react-motion, что означает, что вы получаете все преимущества от библиотеки (т. е. вы можете прерывать анимацию, иметь несколько размонтированных одновременно).
использование:
import Transition from 'react-motion-ui-pack'
<Transition
enter={{ opacity: 1, translateX: 0 }}
leave={{ opacity: 0, translateX: -100 }}
component={false}
>
{ this.state.show &&
<div key="hello">
Hello
</div>
}
</Transition>
Enter определяет конечное состояние компонента; leave-это стиль, применяемый при размонтировании компонента.
вы можете обнаружить, что после того, как вы использовали пакет пользовательского интерфейса пару раз, библиотека react-motion может быть не такой сложной.
Анимация переходов ввода и выхода намного проще с react-move.