【问题标题】:Animate button in React on state changeReact on state change 中的动画按钮
【发布时间】:2019-06-02 02:44:25
【问题描述】:

我正在尝试做一个按钮,在发生某些事情时为标签设置动画(onclick,http 成功/错误)。但我不知道如何应用这些类并找到一种通过组件状态处理它的优雅方式。

单击按钮时,它会从IDLE 转换为BUSY 状态。当 promise 解决 (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);
}

【问题讨论】:

  • 你可以使用 CSS 动画,这是一个例子 - codesandbox.io/s/x2mxr18moq
  • 这不是我想要的。每次状态变化时,我都希望标签向上滑出,新标签向上滑入。

标签: reactjs typescript css-transitions


【解决方案1】:

您可以使用 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

【讨论】:

    猜你喜欢
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 2019-08-03
    • 2021-02-01
    • 2022-08-08
    • 1970-01-01
    • 2020-05-13
    • 2018-09-30
    相关资源
    最近更新 更多