DEV Community

Ryan Dunton
Ryan Dunton

Posted on

Recreating the StateOfJS Survey Animation

The Task

If you follow the world of Javascript, chances are you came across the awesome new "State of Javascript 2018" survey. If not, you can check it out here. I loved the animation at the beginning of it and set about trying to recreate it. Now I will walk through what I did.

My final product can be viewed here. Not ready yet to be viewed on mobile.

Caveat

I have never worked with Greensock inside of React before, so I'm sure this is a really sloppy and brute force way to approach the problem. I encourage other people to give it a go :)

Project Overview

The example we are trying to emulate uses blocks to spell out "State of Javascript 2018" with a button to view the survey. I decided to spell out my name, "Ryan Dunton", and have a button that on hover, allows the user to view my actual name spelled out by the blocks.

I started by setting up an App component to hold my animating blocks. I give the App two state properties "home and margin". The home property will monitor if the elements should be at their starting position and the margin property will allow me to pass the margin property through as props and make it easier to calculate the animation container.

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      home: false,
      margin: 20
    };
  }

Then I gave the App some Letter Components with props for setting up the initial animation

        <Letter
            data="R"
            borderColor="red"
            isHome={this.state.home}
            animationType={"y"}
            currentLeftOrRight={"left"}
            currentTopOrBottom={"top"}
            margin={this.state.margin}
          />

I also add a Button component and pass it a function to change the App's this.state.home to true on hover.

(I'm using Styled Components so if this looks funky you can ignore it)

       <ButtonWrapper>
          <EnterButton
            buttonText="Who am I?"
            checkForHover={this.checkForHover}
            removeHover={this.removeHover}
          />
        </ButtonWrapper>

Going into my Letter Component I need to find store MaxX, MaxY, MinX and MinY value because I am going to send each element to either the Max or Min of one of the axis and generate a random number within a range of the other. I decided to store this data in the Letter Component's state. I also wanted to monitor what "animationType" is being used, basically if it is moving to an X or Y max/min and the current side it is aligned with. I also needed to monitor if the animation was ended and the time/length of the animation. So the initial state of the Letter Component looks like this:

      class Letter extends Component {
        constructor(props) {
        super(props);
        this.state = {
          x: 0,
          y: 0,
          animationTime: 2,
          minTop: 0,
          maxTop: 0,
          maxRight: 0,
          minLeft: 0,
          currentLeftOrRight: null,
          currentTopOrBottom: null,
          animationType: "",
          endAnimation: false
        };

I then need to set these value when the component mounts because I need to calculate its position on the screen. The most difficult part was figuring out the exact container dimensions. This is where passing the margin property through as a prop is helpful because I can reference it in the ComponentDidMount state calculations:

this.setState({
      minTop: -offsetTop - margin,
      maxTop: windowHeight - elementHeight + margin - offsetTop,
      maxRight: windowWidth - elementX - (elementWidth - margin),
      minLeft: -offsetLeft - margin,
      animationType,
      currentLeftOrRight,
      currentTopOrBottom
    });

as well as passing it through to a Styled Component:

const StyledLetter = styled.div`
  margin: ${props => props.margin}px;
`;

So now I have a page that looks like the below with each Letter knowing all the information it needs to do for its animation.

Alt text of image

This is already way too long, so I'll end here and post a followup piece soon!

Top comments (0)