【问题标题】:How to defer this.props.onClick() until after a CSS animation is complete?如何将 this.props.onClick() 推迟到 CSS 动画完成后?
【发布时间】:2017-08-18 00:54:39
【问题描述】:

我正在从我的页面调用自定义 NanoButton 组件以及 onClick 指令以路由到另一个页面:

// Page.js
import { Component } from 'react';
import Router from 'next/router';
class Page2 extends Component {
  render() {
    return(
      <NanoButton type="button" color="success" size="lg" onClick={() => Router.push('/about')}>About</NanoButton>
    )
  }
}

单击按钮(NanoButton 组件)时,我想执行一些内部代码,然后再转到作为道具传入的 onClick。通过这个内部代码,我试图模拟持续 600 毫秒的材料设计涟漪效应。我就是这样做的:

import { Component } from 'react';
import { Button } from 'reactstrap';

class NanoButton extends Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
  }
  onClick(e) {
    this.makeRipple(e);
    this.props.onClick();
  }
  makeRipple(e) {
    const elements = e.target.getElementsByTagName('div');
    while (elements[0]) elements[0].parentNode.removeChild(elements[0]);
    const circle = document.createElement('div');
    e.target.appendChild(circle);
    const d = Math.max(e.target.clientWidth, e.target.clientHeight);
    circle.style.width = `${d}px`;
    circle.style.height = `${d}px`;
    const rect = e.target.getBoundingClientRect();
    circle.style.left = `${e.clientX - rect.left - (d / 2)}px`;
    circle.style.top = `${e.clientY - rect.top - (d / 2)}px`;
    circle.classList.add('ripple');
  }
  render() {
    return (
      <Button
        className={this.props.className}
        type={this.props.type}
        color={this.props.color}
        size={this.props.size}
        onClick={this.onClick}
      >
        {this.props.children}
      </Button>
    );
  }
}

export default NanoButton;

如您所见,我需要在this.props.onClick 之前执行makeRipple 方法。最初,它似乎并没有这样做。然而,经过进一步测试,事实证明这些方法确实以正确的顺序运行,除了路由(如this.props.onClick 中的编码)立即发生并且样式为持续 600 毫秒的波纹动画没有机会跑步。使这个动画发生的 CSS 是:

button {
    overflow: hidden;
    position: relative;
}

button .ripple {
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0.7);
    position: absolute;
    transform: scale(0);
    animation: ripple 0.6s linear;
}

@keyframes ripple {
    to {
        transform: scale(2.5);
        opacity: 0;
    }
}

如何让this.props.onClick 仅在动画完成之后运行?我尝试像这样设置超时:

setTimeout(this.props.onClick(), 600);

但这会引发错误。

注意:我使用NextJS 进行服务器端渲染,如果这有什么不同的话。

【问题讨论】:

  • 你应该通过允许 staterender 完成所有工作来“反应”makeRipple,而不是直接操作 DOM,这在 React 中是不鼓励的。
  • 也试过了,但它不允许我动态设置样式属性(heightwidthtopleft)。我无法在我的div 上使用类似style=${this.state.rippleStyle} 的东西,因为它会引发一个不变的违规错误。有什么反馈吗?
  • 我想破解它的一种方法是让makeRipple 返回一个应用动画的 Promise,然后解决。 onClick(e) { this.makeRipple(e).then(() =&gt; this.props.onClick() ) }makeRipple(e) { return new Promise((resolve) =&gt; { ...do animation... resolve() } )}
  • 但这只有在 makeRipple 没有在 props.onClick 之前执行时才有帮助。就我而言,执行顺序不是问题,只是 onClick 不等待 CSS 完成动画。一旦添加了波纹元素,makeRipple 就完成了,这是我在触发 props.onClick 之前需要等待的动画。
  • 您需要超时来延迟操作。你遇到了什么错误?

标签: reactjs onclick nextjs


【解决方案1】:

有很多方法可以做到这一点,比如Promiseasync/await等。 但是如果你尝试setTimeout,请使用

setTimeout(() => this.props.onClick(), 600);

setTimeout(this.props.onClick, 600);

你的情况:

setTimeout(this.props.onClick(), 600);

不起作用,因为这一行会将this.props.onClick() 的结果传递给第一个参数,而不是传递整个函数。

【讨论】:

  • 就像我在问题中提到的,我已经尝试过 setTimeout。
  • 抱歉,我的意思是setTimeout(this.props.onClick(), 600); 不起作用。您应该将该函数放入setTimeout 的第一个参数中,而不是this.props.onClick() 的结果中。这可能是处理延迟的最简单方法。
  • 哇,你是救生员! setTimeout(() =&gt; this.props.onClick(), 600); 确实有效!您能否在答案中添加几行来解释为什么胖箭头语法在这种情况下有效而不是常规语法?对于将来寻找类似解决方案的其他人来说,这将是一个很大的帮助。再次,非常感谢。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-11
  • 1970-01-01
  • 2015-06-09
  • 2019-10-20
相关资源
最近更新 更多