React-단일 구성 요소의 마운트 및 마운트 해제 애니메이션
이 간단한 일은 쉽게 해낼 수 있어야하지만, 그것이 얼마나 복잡한 지에 대해 머리를 뽑아 내고 있습니다.
내가 원하는 것은 React 컴포넌트의 마운트 및 마운트 해제를 애니메이션하는 것뿐입니다. 지금까지 시도한 내용과 각 솔루션이 작동하지 않는 이유는 다음과 같습니다.
ReactCSSTransitionGroup
-CSS 클래스를 전혀 사용하지 않고 모든 JS 스타일이므로 작동하지 않습니다.ReactTransitionGroup
-이 하위 수준 API는 훌륭하지만 애니메이션이 완료되면 콜백을 사용해야하므로 CSS 전환 만 사용하면 여기서는 작동하지 않습니다. 다음 지점으로 이어지는 애니메이션 라이브러리는 항상 있습니다.- GreenSock-라이선스가 업무용 IMO에 너무 제한적입니다.
- React Motion-이것은 훌륭해 보이지만
TransitionMotion
내가 필요한 것에 대해 매우 혼란스럽고 지나치게 복잡합니다. - 물론 머티리얼 UI처럼 속임수를 쓸 수 있습니다. 요소는 렌더링되지만 숨겨진 상태로 유지되는 (
left: -10000px
)하지만 그 길을 가고 싶지 않습니다. 나는 그것이 해키하다고 생각 하고 내 구성 요소가 마운트 해제되어 DOM을 정리하지 않고 정리 하고 싶습니다.
구현 하기 쉬운 것을 원합니다 . 마운트시 스타일 세트에 애니메이션을 적용합니다. 마운트 해제시 동일한 (또는 다른) 스타일 세트에 애니메이션을 적용합니다. 끝난. 또한 여러 플랫폼에서 고성능이어야합니다.
나는 여기에 벽돌 벽을 쳤다. 누락 된 항목이 있고이를 수행 할 수있는 쉬운 방법이 있으면 알려주세요.
이것은 약간 길지만이 애니메이션을 달성하기 위해 모든 네이티브 이벤트와 메서드를 사용했습니다. 아니 ReactCSSTransitionGroup
, ReactTransitionGroup
등
내가 사용한 것들
- 반응 수명주기 방법
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 animation
}
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 animation
}
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>
Pranesh의 답변에서 얻은 지식을 사용하여 구성 및 재사용 가능한 대체 솔루션을 찾았습니다.
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>
작업하는 동안이 문제에 대응했고, 단순 해 보이지만 실제로는 React에 없습니다. 다음과 같은 것을 렌더링하는 일반적인 시나리오에서 :
this.state.show ? {childen} : null;
로 this.state.show
변경 아이들은 멀리 / 언 마운트 권리가 장착되어있다.
내가 취한 한 가지 접근 방식은 래퍼 구성 요소를 만들고 Animate
다음과 같이 사용하는 것입니다.
<Animate show={this.state.show}>
{childen}
</Animate>
이제 this.state.show
변경 사항으로 소품 변경을 감지 getDerivedStateFromProps(componentWillReceiveProps)
하고 중간 렌더링 단계를 만들어 애니메이션을 수행 할 수 있습니다.
자식이 마운트되거나 마운트 해제 될 때 정적 스테이지에서 시작 합니다.
우리가 감지되면 show
플래그 변경, 우리는 입력 준비 단계 우리가 같은 필요한 특성 계산 height
과 width
에서를 ReactDOM.findDOMNode.getBoundingClientRect()
.
그런 다음 Animate State에 들어가면 css 전환을 사용하여 높이, 너비 및 불투명도를 0에서 계산 된 값으로 (또는 마운트 해제시 0으로) 변경할 수 있습니다.
전환이 끝나면 onTransitionEnd
API를 사용 하여 다시 Static
단계 로 변경 합니다.
단계가 원활하게 전송되는 방법에 대한 자세한 내용이 있지만 전체적인 아이디어 일 수 있습니다.
관심있는 사람이 있다면 React 라이브러리 https://github.com/MingruiZhang/react-animate-mount 를 만들어 솔루션을 공유했습니다. 모든 피드백 환영 :)
다음은 구성 요소의 마운트 해제 단계를 지연시키기 위해이 게시물을 기반으로 하는 새로운 후크 API (TypeScript 포함)를 사용하는 솔루션입니다 .
function useDelayUnmount(isMounted: boolean, delayTime: number) {
const [ shouldRender, setShouldRender ] = useState(false);
useEffect(() => {
let timeoutId: NodeJS.Timeout;
if (isMounted && !shouldRender) {
setShouldRender(true);
}
else if(!isMounted && shouldRender) {
timeoutId = setTimeout(
() => setShouldRender(false),
delayTime
);
}
return () => clearTimeout(timeoutId);
});
return shouldRender;
}
용법:
const Parent: React.FC = () => {
const [ isMounted, setIsMounted ] = useState(true);
const shouldRenderChild = useDelayUnmount(isMounted, 500);
const mountedStyle = {opacity: 1, transition: "opacity 500ms ease-in"};
const unmountedStyle = {opacity: 0, transition: "opacity 500ms ease-in"};
const handleToggleClicked = () => {
setIsMounted(!isMounted);
}
return (
<>
{shouldRenderChild &&
<Child style={isMounted ? mountedStyle : unmountedStyle} />}
<button onClick={handleToggleClicked}>Click me!</button>
</>
);
}
CodeSandbox 링크.
Transition
from을 사용 하는 react-transition-group
것이 마운트 / 마운트 해제를 추적하는 가장 쉬운 방법 이라고 생각 합니다. 매우 유연합니다. 사용이 얼마나 쉬운 지 보여주기 위해 몇 가지 클래스를 사용하고 있지만 addEndListener
, GSAP를 사용하여 많은 운이 좋았던 prop을 사용하여 자신의 JS 애니메이션을 확실히 연결할 수 있습니다 .
샌드 박스 : https://codesandbox.io/s/k9xl9mkx2o
그리고 여기에 내 코드가 있습니다.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Transition } from "react-transition-group";
import styled from "styled-components";
const H1 = styled.h1`
transition: 0.2s;
/* Hidden init state */
opacity: 0;
transform: translateY(-10px);
&.enter,
&.entered {
/* Animate in state */
opacity: 1;
transform: translateY(0px);
}
&.exit,
&.exited {
/* Animate out state */
opacity: 0;
transform: translateY(-10px);
}
`;
const App = () => {
const [show, changeShow] = useState(false);
const onClick = () => {
changeShow(prev => {
return !prev;
});
};
return (
<div>
<button onClick={onClick}>{show ? "Hide" : "Show"}</button>
<Transition mountOnEnter unmountOnExit timeout={200} in={show}>
{state => {
let className = state;
return <H1 className={className}>Animate me</H1>;
}}
</Transition>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
리 액트 모션을 고려하는 사람들에게 단일 구성 요소가 마운트 및 마운트 해제 될 때 애니메이션을 적용하는 것은 설정하는 데 부담이 될 수 있습니다.
이 프로세스를 훨씬 쉽게 시작할 수 있도록하는 react-motion-ui-pack 이라는 라이브러리 가 있습니다. 리 액트 모션을 둘러싼 래퍼입니다. 즉, 라이브러리에서 모든 이점을 얻을 수 있습니다 (즉, 애니메이션을 중단 할 수 있고 동시에 여러 언 마운트를 수행 할 수 있음).
용법:
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는 컴포넌트가 마운트 해제 될 때 적용되는 스타일입니다.
UI 팩을 몇 번 사용한 후에는 react-motion 라이브러리가 더 이상 어렵지 않을 수 있습니다.
react-move를 사용하면 진입 및 종료 전환 애니메이션이 훨씬 쉽습니다 .
여기 내 2cents : 그의 솔루션에 대해 @deckele에게 감사드립니다. 내 솔루션은 그의 기반이며 완전히 재사용 가능한 stateful의 구성 요소 버전입니다.
여기 내 샌드 박스 : https://codesandbox.io/s/302mkm1m .
여기 내 snippet.js :
import ReactDOM from "react-dom";
import React, { Component } from "react";
import style from "./styles.css";
class Tooltip extends Component {
state = {
shouldRender: false,
isMounted: true,
}
shouldComponentUpdate(nextProps, nextState) {
if (this.state.shouldRender !== nextState.shouldRender) {
return true
}
else if (this.state.isMounted !== nextState.isMounted) {
console.log("ismounted!")
return true
}
return false
}
displayTooltip = () => {
var timeoutId;
if (this.state.isMounted && !this.state.shouldRender) {
this.setState({ shouldRender: true });
} else if (!this.state.isMounted && this.state.shouldRender) {
timeoutId = setTimeout(() => this.setState({ shouldRender: false }), 500);
() => clearTimeout(timeoutId)
}
return;
}
mountedStyle = { animation: "inAnimation 500ms ease-in" };
unmountedStyle = { animation: "outAnimation 510ms ease-in" };
handleToggleClicked = () => {
console.log("in handleToggleClicked")
this.setState((currentState) => ({
isMounted: !currentState.isMounted
}), this.displayTooltip());
};
render() {
var { children } = this.props
return (
<main>
{this.state.shouldRender && (
<div className={style.tooltip_wrapper} >
<h1 style={!(this.state.isMounted) ? this.mountedStyle : this.unmountedStyle}>{children}</h1>
</div>
)}
<style>{`
@keyframes inAnimation {
0% {
transform: scale(0.1);
opacity: 0;
}
60% {
transform: scale(1.2);
opacity: 1;
}
100% {
transform: scale(1);
}
}
@keyframes outAnimation {
20% {
transform: scale(1.2);
}
100% {
transform: scale(0);
opacity: 0;
}
}
`}
</style>
</main>
);
}
}
class App extends Component{
render(){
return (
<div className="App">
<button onClick={() => this.refs.tooltipWrapper.handleToggleClicked()}>
click here </button>
<Tooltip
ref="tooltipWrapper"
>
Here a children
</Tooltip>
</div>
)};
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
2019 년에 로딩 스피너를 만드는 동안이 문제를 해결 한 방법은 다음과 같습니다. React 기능 구성 요소를 사용하고 있습니다.
하위 Spinner 구성 요소 가있는 상위 앱 구성 요소가 있습니다 .
앱 에는 앱이로드 중인지 여부에 대한 상태가 있습니다. 앱이로드되면 Spinner 가 정상적으로 렌더링됩니다. 앱이로드되지 않을 때 ( isLoading
false) Spinner 가 prop로 렌더링됩니다 shouldUnmount
.
App.js :
import React, {useState} from 'react';
import Spinner from './Spinner';
const App = function() {
const [isLoading, setIsLoading] = useState(false);
return (
<div className='App'>
{isLoading ? <Spinner /> : <Spinner shouldUnmount />}
</div>
);
};
export default App;
스피너 에는 숨겨진 여부에 대한 상태가 있습니다. 처음에는 기본 소품과 상태를 사용하여 Spinner 가 정상적으로 렌더링됩니다. Spinner-fadeIn
클래스는 페이딩이 애니메이션. 때 스피너 소품을 수신 shouldUnmount
가로 렌더링 Spinner-fadeOut
이 페이드 아웃 애니메이션, 대신 클래스입니다.
그러나 나는 또한 페이드 아웃 후 컴포넌트를 마운트 해제하기를 원했습니다.
이 시점 onAnimationEnd
에서 위의 @ pranesh-ravi의 솔루션과 유사한 React 합성 이벤트를 사용해 보았지만 작동하지 않았습니다. 대신 setTimeout
에 애니메이션과 같은 길이의 지연을 사용 하여 상태를 숨김으로 설정했습니다. 로 지연된 후 스피너 가 업데이트 isHidden === true
되고 아무것도 렌더링되지 않습니다.
여기서 핵심은 부모가 자식을 언 마운트하지 않고 언 마운트 할시기를 자식에게 알려주며 자식이 언 마운트 작업을 처리 한 후 스스로 언 마운트한다는 것입니다.
Spinner.js :
import React, {useState} from 'react';
import './Spinner.css';
const Spinner = function(props) {
const [isHidden, setIsHidden] = useState(false);
if(isHidden) {
return null
} else if(props.shouldUnmount) {
setTimeout(setIsHidden, 500, true);
return (
<div className='Spinner Spinner-fadeOut' />
);
} else {
return (
<div className='Spinner Spinner-fadeIn' />
);
}
};
export default Spinner;
Spinner.css :
.Spinner {
position: fixed;
display: block;
z-index: 999;
top: 50%;
left: 50%;
margin: -40px 0 0 -20px;
height: 40px;
width: 40px;
border: 5px solid #00000080;
border-left-color: #bbbbbbbb;
border-radius: 40px;
}
.Spinner-fadeIn {
animation:
rotate 1s linear infinite,
fadeIn .5s linear forwards;
}
.Spinner-fadeOut {
animation:
rotate 1s linear infinite,
fadeOut .5s linear forwards;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
또한 단일 구성 요소 Animation이 절실히 필요했습니다. React Motion을 사용하는 데 지 쳤지 만 사소한 문제로 머리카락을 잡아 당겼습니다 .. (i thing). 인터넷 검색 후 git repo에서이 게시물을 보았습니다. 누군가에게 도움이되기를 바랍니다 ..
출처 & 신용도 참조 . 이것은 지금 나를 위해 작동합니다. 내 사용 사례는로드 및 언로드의 경우 애니메이션 및 언 마운트하는 모달이었습니다.
class Example extends React.Component {
constructor() {
super();
this.toggle = this.toggle.bind(this);
this.onRest = this.onRest.bind(this);
this.state = {
open: true,
animating: false,
};
}
toggle() {
this.setState({
open: !this.state.open,
animating: true,
});
}
onRest() {
this.setState({ animating: false });
}
render() {
const { open, animating } = this.state;
return (
<div>
<button onClick={this.toggle}>
Toggle
</button>
{(open || animating) && (
<Motion
defaultStyle={open ? { opacity: 0 } : { opacity: 1 }}
style={open ? { opacity: spring(1) } : { opacity: spring(0) }}
onRest={this.onRest}
>
{(style => (
<div className="box" style={style} />
))}
</Motion>
)}
</div>
);
}
}
이 작업 은 앞서 언급 한 라이브러리와 같은의 CSSTransition
구성 요소를 사용하여 쉽게 수행 할 수 있습니다 react-transition-group
. 트릭은 당신이 CSSTransition 구성 요소를 포장 할 필요가있다 당신이 일반적으로 것처럼 표시 / 숨기기 메커니즘없이 .IE {show && <Child>}...
그렇지 않으면 당신이 숨어 애니메이션을 하고 작동하지 않습니다. 예:
ParentComponent.js
import React from 'react';
import {CSSTransition} from 'react-transition-group';
function ParentComponent({show}) {
return (
<CSSTransition classes="parentComponent-child" in={show} timeout={700}>
<ChildComponent>
</CSSTransition>
)}
ParentComponent.css
// animate in
.parentComponent-child-enter {
opacity: 0;
}
.parentComponent-child-enter-active {
opacity: 1;
transition: opacity 700ms ease-in;
}
// animate out
.parentComponent-child-exit {
opacity: 1;
}
.parentComponent-child-exit-active {
opacity: 0;
transition: opacity 700ms ease-in;
}
참고 URL : https://stackoverflow.com/questions/40064249/react-animate-mount-and-unmount-of-a-single-component
'IT박스' 카테고리의 다른 글
사람이 읽을 수 있고 사용할 수있는 짧지 만 고유 한 ID 생성 (0) | 2020.10.27 |
---|---|
경고 : 원격 HEAD는 존재하지 않는 참조를 참조하므로 체크 아웃 할 수 없습니다. (0) | 2020.10.27 |
ILMerge를 Visual Studio 빌드 프로세스에 통합하여 어셈블리를 병합하는 방법은 무엇입니까? (0) | 2020.10.27 |
numpy 배열의 n 번째 항목마다 서브 샘플링 (0) | 2020.10.27 |
HTTP 양식에서 HTTPS로 제출하는 것이 안전합니까? (0) | 2020.10.27 |