I am trying to animate an object with css in react and change the animation (timing) dynamically, how can I set the style after animation end?

Viewed 220

I am trying to make a hot air balloon styled button that floats (translates over x and y space). Currently I am using CSS variable to set animation properties but I can only do this once or else I get an error saying
"TypeError: Cannot assign to read only property '--animation-time' of object '#'"
How do I work around this to be able to set new random properties for where the animation starts, ends, and how long it lasts? Currently I have this:

class HotAir extends React.Component{
  constructor(props){  
    super(props);  
    this.state = {
      link: props.link,
      cls: props.cls,
      img: props.img,
      text: props.text,
    }  
  }  

  render(){
    var cssProperties = {};
    cssProperties['--animation-time'] = Math.random()*10 + 's';
    console.log(cssProperties);

    
    return(
      <div className="balloon" style={cssProperties} onAnimationEnd={()=> cssProperties['--animation-time'] = '60s'}>
        <Link to={this.state.link}><button className="balloonbutt">
          <img src={this.state.img} alt=""></img>
          <div className="balloontext">{this.state.text}</div></button>
        </Link>
      </div>
    );
  }
}

and this is my relevant css

.balloon{
    --animation-time: 60s;
    --x-float-start: 10vw;
    --y-float-start: 10vh;
    --x-float-end: 20vw;
    --y-float-end: 20vh;
    position: relative;
  text-align: center;
  color: white;
    animation: float var(--animation-time) linear;
}
@keyframes float{
    0%{
        left: var(--x-float-start);
        top: var(--y-float-start);
    }
    100%{
        left: var(--x-float-end);
        top: var(--y-float-end);
    }
}
1 Answers

I have modified your code in more Reacty way. You were using a var object and mutating the data which is not how React expects things to be done. I have updated my code and hopefully your linting tool won't complain after the changes.

Changes:

  1. Added cssProperties to state so react will be aware of that property.
  2. Instead of mutating the value on animation end, updated the state with,

this.setState({ ...this.state, cssProperties: { '--animation-time': '60' } });

import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';

interface AppProps {}
interface AppState {
  name: string;
  cssProperties;
}

class App extends Component<AppProps, AppState> {
  constructor(props) {
    super(props);
    this.state = {
      name: 'React',
      cssProperties: {
    '--animation-time': Math.random() * 10 + 's',
    '--x-float-start': Math.trunc(Math.random() * 200) - 10 + 'px',
    '--y-float-start': Math.trunc(Math.random() * 200) - 10 + 'px',
    '--x-float-end': Math.trunc(Math.random() * 200) - 10 + 'px',
    '--y-float-end': Math.trunc(Math.random() * 200) - 10 + 'px'
  }
    };
  }

  render() {
    console.log(this.state.cssProperties);
    return (
      <div
        className="balloon"
        style={this.state.cssProperties}
        onAnimationIteration={() => {
          console.log(this.state.cssProperties);
      this.setState({
        ...this.state,
        cssProperties: {
          ...this.state.cssProperties,
          '--x-float-start': this.state.cssProperties['--x-float-end'],
          '--y-float-start': this.state.cssProperties['--y-float-end'],
          '--x-float-end': Math.trunc(Math.random() * 200) - 10 + 'px',
          '--y-float-end': Math.trunc(Math.random() * 200) - 10 + 'px'
        }
      });
          
        }}
      >
        <Hello name={this.state.name} />
        <p>Start editing to see some magic happen :)</p>
      </div>
    );
  }
}

render(<App />, document.getElementById('root'));

Update

Since you need an infinite animation, your CSS should have that option.

 animation: float var(--animation-time) linear infinite;
Related