goulashsoup
goulashsoup

Reputation: 3076

React/CSS - Animate button width resize after content changes when width is auto

I have a react component representing a button which changes the content and color depending on some props. If a form component includes this button and gets submitted the button changes it's content and color. This works well.

Now I want an animation (CSS transition) for the changing with when the content of the button changes. Setting an explicit width for different states is NOT an option because the application is multilingual. When using max-width the animation only works when the button extends but not when it decreases.

Here is a snippet with a button component which changes the state every second. Like in the real component this one uses the bootstrap btn classes:

class Button extends React.Component {
  constructor(props) {
    super(props);
    this.state = {status: 0};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  tick() {
    let status = this.state.status;
    status++;
    if(status >= 2) {
      status = 0;
    }
    this.setState({
      status: status
    });
  }

  render() {
    const btnClass = this.state.status ? 'primary' : 'secondary';
    
    return (
      <button className={`btn btn-${btnClass}`} type='button'>
        {this.state.status ? this.props.large : this.props.short}
      </button>
    );
  }
}

// Render it
ReactDOM.render(
  <Button large='This is a long button you can click on!' short='short'/>,
  document.getElementById('react')
);
.btn {
    -webkit-transition: width 1s; /* Safari */
    transition: max-width 1s;
    width: auto;
}

.btn-primary {
  max-width: 300px;
}

.btn-secondary {
  max-width: 50px;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

How can i get an animation for the changing width (when the button gets smaller again)?

Please consider that an answer doesn't have to be just CSS related. I thought about that the react component calculates it's width and then sets it inside the componentWillMount function but that didn't work either...

Upvotes: 4

Views: 8524

Answers (2)

Kostas Siabanis
Kostas Siabanis

Reputation: 3039

Try setting width: 100% to .btn class. There is nothing wrong with animating max-width property. The issue arises when the text is changing from a small text to a larger one and the browser will have to recalculate what auto means as a value for the width property. It seems that 100% does not work the same way.

class Button extends React.Component {
  constructor(props) {
    super(props);
    this.state = {status: 0};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  tick() {
    let status = this.state.status;
    status++;
    if(status >= 2) {
      status = 0;
    }
    this.setState({
      status: status
    });
  }

  render() {
    const btnClass = this.state.status ? 'primary' : 'secondary';
    
    return (
      <button className={`btn btn-${btnClass}`} type='button'>
        {this.state.status ? this.props.large : this.props.short}
      </button>
    );
  }
}

// Render it
ReactDOM.render(
  <Button large='This is a long button you can click on!' short='short'/>,
  document.getElementById('react')
);
.btn {
    -webkit-transition: width 1s; /* Safari */
    transition: max-width 1s;
    width: 100%;
}

.btn-primary {
  max-width: 300px;
}

.btn-secondary {
  max-width: 50px;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

Upvotes: 3

Joey Gough
Joey Gough

Reputation: 3103

I have a solution. Add min-width to your css classes and transition as below.

Animate on the way up. Animate on the way down.

class Button extends React.Component {
  constructor(props) {
    super(props);
    this.state = {status: 0};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  tick() {
    let status = this.state.status;
    status++;
    if(status >= 2) {
      status = 0;
    }
    this.setState({
      status: status
    });
  }

  render() {
    const btnClass = this.state.status ? 'primary' : 'secondary';
    
    return (
      <button className={`btn btn-${btnClass}`} type='button'>
        {this.state.status ? this.props.large : this.props.short}
      </button>
    );
  }
}

// Render it
ReactDOM.render(
  <Button large='This is a long button you can click on!' short='short'/>,
  document.getElementById('react')
);
.btn {
    -webkit-transition: width 1s; /* Safari */
     transition: max-width 1s, min-width 1s;
     width: auto;
}

.btn-primary {
  max-width: 330px;
  min-width: 230px;
}

.btn-secondary {
  max-width: 60px;
  min-width: 40px;
  text-align: left;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

Upvotes: 1

Related Questions