状态更改时的React中的动画按钮

时间:2019-01-07 12:18:30

标签: reactjs typescript css-transitions

我正在尝试创建一个按钮,以在发生某些事件(onclick,http成功/错误)时为标签添加动画。但是我无法弄清楚如何应用这些类并找到一种通过组件状态处理它的优雅方法。

单击按钮时,它会从IDLE转换为BUSY状态。承诺何时解决(SUCCESS)或拒绝(FAILURE)。 3秒后,它重置为IDLE

我正在尝试根据状态切换className。但是它不起作用,我不知道如何正确地做。我不确定是否应该(以及如何)使用react-transition-group插件来顺利完成此操作,因为该组件没有条件地安装/卸载。

也许我应该使用CSS动画而不是CSS过渡。这会是更好的方法吗?

此处的实时示例:https://codesandbox.io/s/yjl5o5vr4v

这是我的代码:

SubmitButton.tsx

import React from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { IconProp } from '@fortawesome/fontawesome-svg-core'
import classNames from 'classnames'
import './SubmitButton.css'

// flag to switch whether dummy request should succeed or fail
let workSuccess = true

interface IProps {}
interface IState {
  status: 'IDLE' | 'BUSY' | 'SUCCESS' | 'FAILURE'
  label: string
  icon?: IconProp
}

const initialState: IState = {
  status: 'IDLE',
  label: 'Click',
  icon: undefined,
}

class SubmitButton extends React.PureComponent<IProps, IState> {
  public state: IState = { ...initialState }
  public render() {
    const { label, icon, status } = this.state

    return (
      <button className="SubmitButton" onClick={this.handleClick}>
        <span
          className={classNames({
            'animation--after': status === 'BUSY',
            'animation--before': status === 'SUCCESS' || status === 'FAILURE',
            ['animation']: status === 'IDLE',
          })}
        >
          {icon && (
            <FontAwesomeIcon className="SubmitButton__icon" icon={icon} />
          )}
          {label}
        </span>
      </button>
    )
  }

  private handleClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
    const { status } = this.state
    if (status === 'IDLE') {
      // animation: 'roll up' (out of view)

      // animation: reset position
      this.setState({
        label: 'Loading...',
        icon: 'spinner',
        status: 'BUSY',
      })

      // animation: 'roll up' (into view)

      try {
        const response = await this.doWork()
        // animation: 'roll up' (out of view)

        if (response) {
          // animation: reset position
          this.setState({
            label: 'OK',
            icon: 'check',
            status: 'SUCCESS',
          })
          // animation: 'roll up' (into view)
        }
      } catch (err) {
        // animation: reset position
        this.setState({
          label: 'ERROR',
          icon: 'exclamation',
          status: 'FAILURE',
        })
        // animation: 'roll up' (into view)
      } finally {
        // animation: reset position
        this.setState({ ...initialState })
        // animation: 'roll up' (into view)
      }
    }
  }

  private doWork = () => {
    return new Promise<boolean>((resolve: any, reject: any) => {
      setTimeout(() => {
        if (workSuccess) {
          resolve(true)
        } else {
          reject('An error happened...')
        }

        workSuccess = !workSuccess
      }, 3000)
    })
  }
}

export default SubmitButton

SubmitButton.css

.SubmitButton {
  overflow: hidden;
  cursor: pointer;
  outline: none;
  font-size: 16px;
  padding: 12px 32px;
}

.SubmitButton__icon {
  margin-right: 12px;
}

.animation {
  display: inline-block;
  transition: transform 300ms ease-out;
  transform: translateY(0);
}
.animation--before {
  transition: transform 300ms ease-out;
  transform: translateY(50px);
}
.animation--after {
  transition: transform 300ms ease-out;
  transform: translateY(-50px);
}

1 个答案:

答案 0 :(得分:0)

您可以使用gif图像代替FontAwesome图标。请检查以下代码。

目前,我以低质量的gif为例,但是您可以从项目中加载优质的gif。

 class SubmitButton extends React.PureComponent<IProps, IState> {
  public state: IState = { ...initialState };
  public render() {
    const { label, icon, status } = this.state;

    return (
      <>
        <button className="SubmitButton" onClick={this.handleClick}>
          <span
            className={classNames({
              "animation--after": status === "BUSY",
              "animation--before": status === "SUCCESS" || status === "FAILURE",
              ["animation"]: status === "IDLE"
            })}
          >
            {icon && (
              <img src="http://www.ajaxload.info/cache/FF/FF/FF/00/00/00/1-0.gif" />
            )}
            {label}
          </span>
        </button>
      </>
    );
  }

Demo