animation - لجوجل - فلاش بلاير للاندرويد 2017




رد-تنشيط جبل وإلغاء تحميل مكون واحد (8)

يجب إنجاز شيء بسيط بهذه السهولة ، لكنني أسحب شعري من مدى تعقيده.

كل ما أريد القيام به هو تنشيط وإلغاء تثبيت مكون React ، هذا كل شيء. إليك ما جربته حتى الآن ولماذا لا يعمل كل حل:

  1. ReactCSSTransitionGroup - أنا لا أستخدم فئات CSS على الإطلاق ، إنها كلها أنماط JS ، لذلك لن ينجح هذا.
  2. ReactTransitionGroup - واجهة برمجة تطبيقات المستوى الأدنى هذه رائعة ، لكنها تتطلب منك استخدام رد اتصال عند اكتمال الرسوم المتحركة ، لذلك لن يعمل استخدام انتقالات CSS هنا. هناك دائمًا مكتبات للرسوم المتحركة ، والتي تؤدي إلى النقطة التالية:
  3. GreenSock - الترخيص مقيد للغاية بالنسبة لاستخدام الأعمال IMO.
  4. React Motion - يبدو هذا رائعًا ، لكن TransitionMotion مربكة للغاية ومعقدة للغاية لما أحتاج إليه.
  5. بالطبع يمكنني فقط أن أفعل الخداع مثلما يفعل Material UI ، حيث يتم تقديم العناصر ولكن تظل مخفية ( left: -10000px ) لكنني أفضل عدم السير في هذا الطريق. أنا أعتبر هذا مخترقًا ، وأريد إلغاء تثبيت المكونات الخاصة بي حتى يتم تنظيفها وعدم تكتل DOM.

أريد شيئًا سهل التنفيذ. على جبل ، تحريك مجموعة من الأساليب. عند إلغاء التثبيت ، قم بتحريك نفس مجموعة الأنماط (أو آخر). منجز. كما يجب أن يكون الأداء العالي على منصات متعددة.

لقد اصطدمت بجدار من الطوب هنا. إذا كنت أفتقد شيئًا وكانت هناك طريقة سهلة للقيام بذلك ، فأعلمني بذلك.


أعتقد أن استخدام Transition من مجموعة react-transition-group ربما يكون أسهل طريقة لتتبع التركيب / إلغاء التثبيت. انها مرنة بشكل لا يصدق. أنا أستخدم بعض الفئات لإظهار مدى سهولة استخدامها ولكن يمكنك بالتأكيد ربط الرسوم المتحركة JS الخاصة بك باستخدام addEndListener prop - والتي حظيت كثيرًا من الحظ باستخدام GSAP مع ذلك.

صندوق الحماية: 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);

إليك الحل الخاص بي باستخدام API hooks الجديدة (مع 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 .


إليكم سنتان: بفضلdeckele لحله. يعتمد حلي على إصداره ، فهو إصدار مكون من حالة الحالة ، يمكن إعادة استخدامه بالكامل.

هنا رمل بلدي: 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);


بالنسبة لأولئك الذين يفكرون في حركة رد الفعل ، فإن تحريك مكون واحد عندما يتصاعد ويفكّ يمكن أن يكون أمرًا ساحقًا في الإعداد.

هناك مكتبة تدعى 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 الحالة النهائية للمكون ؛ الإجازة هي النمط الذي يتم تطبيقه عند إلغاء تثبيت المكون.

قد تجد أنه بمجرد استخدام حزمة واجهة المستخدم عدة مرات ، قد لا تصبح مكتبة حركة التفاعل هائلة.


كنت أيضا في حاجة ماسة للرسوم المتحركة مكون واحد. لقد تعبت من استخدام React Motion ولكني كنت أسحب شعري لمثل هذه المشكلة التافهة .. (أنا شيء). بعد بعض googling جئت عبر هذا المنصب على 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>
    );
  }
}


هذا طويل بعض الشيء لكنني استخدمت كل الأحداث والأساليب المحلية لتحقيق هذه الرسوم المتحركة. لا توجد ReactCSSTransitionGroup و ReactTransitionGroup وغيرها.

الأشياء التي استخدمتها

  • رد أساليب دورة الحياة
  • حدث onTransitionEnd

كيف يعمل هذا

  • قم بتركيب العنصر على أساس تمرير دعامة ( mounted ) ومع نمط افتراضي ( opacity: 0 )
  • بعد التحميل أو التحديث ، استخدم componentDidMount ( componentWillReceiveProps لمزيد من التحديثات) لتغيير النمط ( opacity: 1 ) مع مهلة (لجعله غير متزامن).
  • أثناء إلغاء التثبيت ، مرر دعامة إلى المكون لتحديد إلغاء onTransitionEnd ، قم بتغيير النمط مرة أخرى ( 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>


يمكن القيام بذلك بسهولة باستخدام مكون 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; } 




react-motion