How to defer this.props.onClick() until after a CSS animation is complete?

筅森魡賤 提交于 2019-12-11 10:58:23

问题


I am calling a custom NanoButton component from my page along with an onClick instruction to route to another page:

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

When the button (NanoButton component) is clicked, I want to execute some internal code before moving on to the onClick coming in as props. Through this internal code, I am trying to simulate the material-design ripple effect that lasts 600 milliseconds. This is how I do it:

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;

So as you can see, I need the makeRipple method to execute before this.props.onClick. And initially, it didn't seem to doing that. However, after further testing, it turns out that the methods do run in the right order after all, except the routing (as coded in this.props.onClick) happens instantly and the ripple animation that's styled to last 600 milliseconds doesn't get a chance to run. The CSS that makes this animation happen is:

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

How do I make the this.props.onClick run only AFTER the animation is complete? I tried setting a timeout like so:

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

But that throws an error.

Note: I'm using NextJS for server side rendering, if that makes any difference.


回答1:


There are a lot of ways to do it, like Promise, async/await, etc. But if you try setTimeout please use

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

or

setTimeout(this.props.onClick, 600);

your case:

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

won't work because this line will pass the result of this.props.onClick() into the first param instead of passing the whole function.



来源:https://stackoverflow.com/questions/45746939/how-to-defer-this-props-onclick-until-after-a-css-animation-is-complete

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!