React - tek bir bileşenin takılıp çıkarılmasını canlandırın


103

Bu kadar basit bir şey kolayca başarılmalı, yine de saçlarımı ne kadar karmaşık olduğu konusunda çekiyorum.

Tek yapmak istediğim, bir React bileşeninin montajını ve sökülmesini canlandırmak, hepsi bu. Şimdiye kadar denediklerim ve her çözümün neden işe yaramayacağı aşağıda açıklanmıştır:

  1. ReactCSSTransitionGroup - CSS sınıflarını hiç kullanmıyorum, hepsi JS stilleri, yani bu işe yaramayacak.
  2. ReactTransitionGroup- Bu düşük seviyeli API harikadır, ancak animasyon tamamlandığında bir geri arama kullanmanızı gerektirir, bu nedenle sadece CSS geçişlerini kullanmak burada çalışmaz. Her zaman bir sonraki noktaya götüren animasyon kitaplıkları vardır:
  3. GreenSock - Lisanslama, ticari kullanım IMO'su için çok kısıtlayıcıdır.
  4. React Motion - Bu harika görünüyor, ancak TransitionMotionson derece kafa karıştırıcı ve ihtiyacım olan şey için fazlasıyla karmaşık.
  5. Tabii ki, Materyal UI'nin yaptığı gibi, öğelerin işlendiği ancak gizli kaldığı ( left: -10000px) hileler yapabilirim, ancak bu yola gitmeyi tercih etmem. Bunu hacky düşünün ve ben istiyorum onlar temizlemek ve DOM doldurmasını değildir bu nedenle bağlantısını kesmesine benim bileşenleri.

Uygulaması kolay bir şey istiyorum . Montajda, bir dizi stili canlandırın; unmount'ta, aynı (veya başka) stil kümesini canlandırın. Bitti. Aynı zamanda birden çok platformda yüksek performanslı olması gerekir.

Burada bir tuğla duvara çarptım. Bir şeyi kaçırıyorsam ve bunu yapmanın kolay bir yolu varsa bana bildirin.


Burada ne tür bir animasyondan bahsediyoruz?
Pranesh Ravi

CSS opaklığının yavaş yavaş transform: scale
belirmesi

1. ve 2. nokta kafamı karıştırıyor. Ne tür animasyonlar kullanıyorsunuz? JS geçişleri mi yoksa CSS geçişleri mi?
Pranesh Ravi

1
CSS stillerini / sınıflarını (örneğin .thing { color: #fff; }) JS stilleriyle ( const styles = { thing: { color: '#fff' } })) karıştırmayın
ffxsam

Ancak sorun şu ki, javascript kullanarak stili değiştirmeye çalıştığınızda, aslında herhangi bir geçiş sağlamayan bir öğenin stilini değiştiriyorsunuz.
Pranesh Ravi

Yanıtlar:


104

Bu biraz uzun ama bu animasyonu elde etmek için tüm yerel olayları ve yöntemleri kullandım. Hayır ReactCSSTransitionGroup, ReactTransitionGroupvb.

Kullandığım şeyler

  • Yaşam döngüsü yöntemlerine tepki verme
  • onTransitionEnd Etkinlik

Bu nasıl çalışır

  • Öğeyi, geçirilen montaj desteğine ( mounted) ve varsayılan stile ( opacity: 0) göre monte edin
  • Bağlama veya güncellemeden sonra , stili ( ) bir zaman aşımı ile değiştirmek ( eşzamansız hale getirmek için ) için componentDidMount( componentWillReceivePropsdaha fazla güncelleme için) kullanın opacity: 1.
  • Unmount sırasında, ayırmayı belirlemek için bileşene bir destek verin, stili tekrar değiştirin ( opacity: 0), onTransitionEndöğeyi DOM'den ayırın.

Döngüye devam edin.

Kodu gözden geçirin, anlayacaksınız. Herhangi bir açıklamaya ihtiyaç duyarsanız, lütfen bir yorum bırakın.

Bu yardımcı olur umarım.

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>


Bunun için teşekkürler! Nereden öğrendin onTransitionEnd? React belgelerinde göremiyorum.
ffxsam

@ffxsam facebook.github.io/react/docs/events.html Geçiş etkinlikleri kapsamındadır.
Pranesh Ravi

1
Yine de ne yaptığını nasıl bildin, belgeler hiçbir şeyi açıklamıyor. Başka bir soru: bir componentWillReceivePropsşeyi geri verebileceğini nasıl bildin ? Bununla ilgili daha fazla bilgiyi nerede okuyabilirim?
ffxsam

1
@ffxsam onTransitionEnd yerel bir JavaScript olayıdır. Bunun hakkında google'da dolaşabilirsiniz. facebook.github.io/react/docs/… size componentWillReceiveProps hakkında bir fikir verecektir.
Pranesh Ravi

7
BTW Kodunuzda bir hata olduğunu düşünüyorum. Senin içinde Parentbileşen, başvurabileceğinizthis.transitionEnd
ffxsam

15

Pranesh'in cevabından edindiğim bilgileri kullanarak, yapılandırılabilir ve yeniden kullanılabilir alternatif bir çözüm buldum:

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

Kullanım:

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

Ve son olarak, başka bir bileşenin renderyönteminde:

return <div>
  <ReactTransitionGroup>
    <Thing />
  </ReactTransitionGroup>
</div>

1
Ve @ffxsam'i nasıl bağlar / ayırırsınız?

Nasıl componentWillLeave()ve nasıl componentWillEnter()çağrılıyor AnimatedMount?
Rokit

Benim için işe yaramıyor
alanım

15

İşte benim çözümüm , bileşenin ayrılma aşamasını geciktirmek için bu yazıyı temel alan yeni kanca API'sini (TypeScript ile) kullanarak :

function useDelayUnmount(isMounted: boolean, delayTime: number) {
    const [ shouldRender, setShouldRender ] = useState(false);

    useEffect(() => {
        let timeoutId: number;
        if (isMounted && !shouldRender) {
            setShouldRender(true);
        }
        else if(!isMounted && shouldRender) {
            timeoutId = setTimeout(
                () => setShouldRender(false), 
                delayTime
            );
        }
        return () => clearTimeout(timeoutId);
    }, [isMounted, delayTime, shouldRender]);
    return shouldRender;
}

Kullanım:

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 bağlantısı.


2
zarif çözüm, bazı yorumlar eklediyseniz harika olur :)
Webwoman

javascript uzantısında iyi çalıştığı için neden typescrypt uzantısını kullanasınız?
Webwoman

ayrıca konsolunuz "ad alanı NodeJS zaman aşımı bulunamıyor"
döndürüyor

1
@Webwoman Yorumlarınız için teşekkürler. Bildirdiğiniz sorunu "NodeJS zaman aşımı" ile yeniden oluşturamıyorum, cevabın altındaki CodeSandbox bağlantıma bakın. TypeScript ile ilgili olarak, kişisel olarak JavaScript yerine kullanmayı tercih ediyorum, ancak her ikisi de elbette geçerli.
deckele

10

Çalışmam sırasında bu soruna karşı koydum ve göründüğü kadar basit, aslında React'te değil. Şunun gibi bir şey oluşturduğunuz normal bir senaryoda:

this.state.show ? {childen} : null;

olarak this.state.showdeğişikliklere çocuklar uzak / monte edilmemiş hakkı monte edilmiştir.

Benim aldığım yaklaşımlardan biri, bir sarmalayıcı bileşeni oluşturmak Animateve bunu şu şekilde kullanmaktır:

<Animate show={this.state.show}>
  {childen}
</Animate>

artık this.state.showdeğişiklikler olarak, prop değişikliklerini algılayabilir getDerivedStateFromProps(componentWillReceiveProps)ve animasyonları gerçekleştirmek için ara oluşturma aşamaları oluşturabiliriz.

Bir sahne döngüsü şöyle görünebilir

Çocuklar monte edildiğinde veya söküldüğünde Static Stage ile başlarız .

Tespit ettiğimiz kez showbayrak değişiklikleri, biz girmek Hazırlık Maçı biz gibi gerekli özelliklerin hesaplanması heightve widthgelen ReactDOM.findDOMNode.getBoundingClientRect().

Daha sonra Animate Durumuna girerek yüksekliği, genişliği ve opaklığı 0'dan hesaplanan değerlere (veya sökme ise 0'a) değiştirmek için css geçişini kullanabiliriz.

Geçişin sonunda, sahneye onTransitionEndgeri dönmek için api kullanırız Static.

Aşamaların sorunsuz bir şekilde nasıl aktarıldığına dair çok daha fazla ayrıntı var ama bu genel fikir olabilir :)

İlgilenen biri varsa, çözümümü paylaşmak için bir React kitaplığı https://github.com/MingruiZhang/react-animate-mount oluşturdum . Herhangi bir geri bildirim hoş geldiniz :)


Geri bildiriminiz için teşekkür ederiz, daha önceki kaba cevap için özür dilerim. Cevabıma daha fazla ayrıntı ve bir şema ekledim, umarım bu başkalarına daha yararlı olur.
Mingrui Zhang

2
@MingruiZhang Yorumları olumlu karşıladığınızı ve cevabınızı geliştirdiğinizi görmek güzel. Görmek çok ferahlatıcı. İyi iş.
Bugs

6

Sanırım Transitionfrom kullanmanın react-transition-groupmuhtemelen montaj / demontajı izlemenin en kolay yolu. İnanılmaz derecede esnektir. Kullanımının ne kadar kolay olduğunu göstermek için bazı sınıflar kullanıyorum, ancak kesinlikle kendi JS animasyonlarınızı addEndListenerprop kullanarak bağlayabilirsiniz - GSAP'ı kullanırken de çok şansım oldu.

Korumalı alan: https://codesandbox.io/s/k9xl9mkx2o

Ve işte kodum.

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

1
Tarzlı bileşenler kullanıyorsanız, showpervaneyi basitçe H1stil verilmiş bileşenin içindeki tüm mantığa aktarabilir ve yapabilirsiniz. Beğen ...animation: ${({ show }) => show ? entranceKeyframes : exitKeyframes} 300ms ease-out forwards;
Aleks

3

Çerçeve hareketi

Çerçeve hareketini npm'den yükleyin.

import { motion, AnimatePresence } from "framer-motion"

export const MyComponent = ({ isVisible }) => (
  <AnimatePresence>
    {isVisible && (
      <motion.div
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
      />
    )}
  </AnimatePresence>
)

1

React-motion'u düşünenler için, tek bir bileşene monte edilirken ve çıkarılırken animasyon uygulamak çok zor olabilir.

Bu işlemin başlamasını çok daha kolay hale getiren react-motion-ui-pack adlı bir kitaplık var. Bu, react-motion'u çevreleyen bir sarmalayıcıdır, yani kitaplıktan tüm faydaları elde edersiniz (yani, animasyonu kesebilirsiniz, aynı anda birden fazla unmount olur).

Kullanım:

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, bileşenin son durumunun ne olması gerektiğini tanımlar; ayrılma, bileşen kaldırıldığında uygulanan stildir.

UI paketini birkaç kez kullandıktan sonra, react-motion kitaplığının artık göz korkutucu olmayabileceğini fark edebilirsiniz.


Proje artık sürdürülmüyor (2018)
Micros


1

Bu, bahsettiğiniz kütüphanelerdeki gibi CSSTransitionbileşen kullanılarak kolayca yapılabilir react-transition-group. İşin sırrı CSSTransition bileşeni sarmak gerekiyor ise genellikle olur gibi bir göstermek / gizlemek mekanizması olmadan .ie {show && <Child>}...Aksi saklanıyorsun animasyon ve çalışma olmaz. Misal:

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

0

İşte benim 2 sentim: çözümü için @deckele'ye teşekkürler. Benim çözümüm onun temeline dayanıyor, bu durum bilgisinin bileşen versiyonu, tamamen yeniden kullanılabilir.

işte sanal alanım: https://codesandbox.io/s/302mkm1m .

burada benim 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);

0

İşte 2019'da bir yükleme çarkı yaparken bunu nasıl çözdüm. React fonksiyonel bileşenlerini kullanıyorum.

Alt Döndürücü bileşeni olan bir üst Uygulama bileşenim var .

Uygulama , uygulamanın yüklenip yüklenmediğini belirten bir duruma sahiptir. Uygulama yüklenirken Spinner normal şekilde işlenir. Uygulama yüklenmediğinde ( isLoadingyanlış) Spinner , destek ile oluşturulur 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'ın gizli olup olmadığına dair durumu vardır. Başlangıçta, varsayılan props ve state ile Spinner normal şekilde oluşturulur. Spinner-fadeInSınıf içinde solan o canlandırır. Ne zaman Spinner pervane aldığında shouldUnmountonunla kılan Spinner-fadeOutdışarı solma animasyon yerine sınıfa.

Bununla birlikte, bileşenin söndükten sonra kaldırılmasını da istedim.

Bu noktada onAnimationEnd, @ pranesh-ravi'nin yukarıdaki çözümüne benzer şekilde React sentetik olayını kullanmayı denedim , ancak işe yaramadı. Bunun yerine setTimeout, durumu animasyonla aynı uzunlukta bir gecikmeyle gizlenecek şekilde ayarlardım. Döndürücü , ile gecikmeden sonra güncellenecek isHidden === trueve hiçbir şey işlenmeyecektir.

Buradaki anahtar, ebeveynin çocuğu ayırmaması, çocuğa ne zaman inmesi gerektiğini söylemesi ve çocuğun sökme işini hallettikten sonra kendi kendine ayrılmasıdır.

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

0

Ayrıca tek bileşenli Animasyona da çok ihtiyacım vardı. React Motion'ı kullanmaktan yoruldum ama bu kadar önemsiz bir sorun yüzünden tüylerimi çekiyordum .. (i şey). Biraz googling yaptıktan sonra bu yazıya git repolarında rastladım. Umarım birine yardımcı olur ..

Gönderen ve ayrıca kredi . Bu şu andan itibaren benim için çalışıyor. Kullanım durumum, yükleme ve boşaltma durumunda canlandırmak ve çıkarmak için bir modaldı.

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


0

Burada pek çok cevap olduğunu biliyorum ama yine de ihtiyaçlarıma uygun bir cevap bulamadım. İstiyorum:

  • Fonksiyonel bileşenler
  • Bileşenlerimin takıldıklarında / söküldüklerinde kolayca açılıp kapanmasına izin verecek bir çözüm.

Saatlerce uğraştıktan sonra işe yarayan bir çözümüm var% 90 diyebilirim. Sınırlamayı aşağıdaki kodda bir yorum bloğuna yazdım. Yine de daha iyi bir çözümü çok isterdim, ancak buradaki diğer çözümler de dahil olmak üzere bulduğum en iyisi bu.

const TIMEOUT_DURATION = 80 // Just looked like best balance of silky smooth and stop delaying me.

// Wrap this around any views and they'll fade in and out when mounting /
// unmounting.  I tried using <ReactCSSTransitionGroup> and <Transition> but I
// could not get them to work.  There is one major limitation to this approach:
// If a component that's mounted inside of <Fade> has direct prop changes,
// <Fade> will think that it's a new component and unmount/mount it.  This
// means the inner component will fade out and fade in, and things like cursor
// position in forms will be reset. The solution to this is to abstract <Fade>
// into a wrapper component.

const Fade: React.FC<{}> = ({ children }) => {
  const [ className, setClassName ] = useState('fade')
  const [ newChildren, setNewChildren ] = useState(children)

  const effectDependency = Array.isArray(children) ? children : [children]

  useEffect(() => {
    setClassName('fade')

    const timerId = setTimeout(() => {
      setClassName('fade show')
      setNewChildren(children)
    }, TIMEOUT_DURATION)

    return () => {
      clearTimeout(timerId)
    }   

  }, effectDependency)

  return <Container fluid className={className + ' p-0'}>{newChildren}</Container>
}

Açmak / kapatmak istediğiniz bir bileşeniniz varsa, onu <Fade>Örn. <Fade><MyComponent/><Fade>.

Bunun react-bootstrapsınıf adları ve for için kullanıldığını <Container/>, ancak her ikisinin de kolayca özel CSS ve normal bir eski ile değiştirilebileceğini unutmayın <div>.


0

Düğümü doğrudan canlandırmak için Velocityveya AnimeJSkitaplık kullanırsam ( cssveya yerine setTimeout), hookanimasyonu başlatmak için animasyon durumunu onve işlevini sağlamak için bir a tasarlayabileceğimi öğrendim onToggle(örn. Aşağı kayma, solma).

Temel olarak kancanın yaptığı şey animasyonu açıp kapatmak ve daha sonraon buna göre güncellemek . Böylelikle animasyonun durumunu doğru bir şekilde alabiliyoruz. Bunu yapmadan anlık olarak cevap verecekti duration.

/**
 * A hook to provide animation status.
 * @class useAnimate
 * @param {object} _                props
 * @param {async} _.animate         Promise to perform animation
 * @param {object} _.node           Dom node to animate
 * @param {bool} _.disabled         Disable animation
 * @returns {useAnimateObject}      Animate status object
 * @example
 *   const { on, onToggle } = useAnimate({
 *    animate: async () => { },
 *    node: node
 *  })
 */

import { useState, useCallback } from 'react'

const useAnimate = ({
  animate, node, disabled,
}) => {
  const [on, setOn] = useState(false)

  const onToggle = useCallback(v => {
    if (disabled) return
    if (v) setOn(true)
    animate({ node, on: v }).finally(() => {
      if (!v) setOn(false)
    })
  }, [animate, node, disabled, effect])

  return [on, onToggle]
}

export default useAnimate

Kullanım aşağıdaki gibidir,

  const ref = useRef()
  const [on, onToggle] = useAnimate({
    animate: animateFunc,
    node: ref.current,
    disabled
  })
  const onClick = () => { onToggle(!on) }

  return (
      <div ref={ref}>
          {on && <YOUROWNCOMPONENT onClick={onClick} /> }
      </div>
  )

ve animasyon uygulaması şöyle olabilir:

import anime from 'animejs'

const animateFunc = (params) => {
  const { node, on } = params
  const height = on ? 233 : 0
  return new Promise(resolve => {
    anime({
      targets: node,
      height,
      complete: () => { resolve() }
    }).play()
  })
}



0

React yaşam döngüsü yöntemlerini her zaman kullanabilirsiniz, ancak react-transition-group, ister kullanıyor styled-componentsister sade css , karşılaştığım animasyonlar için en uygun kitaplıktır . Bileşeninizin montajını ve sökülmesini izlemek ve buna göre animasyonları oluşturmak istediğinizde özellikle kullanışlıdır. TransitionTarzlı bileşenlerle ve CSSTransitiondüz css sınıf adları kullandığınızda kullanın .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.