<div id='app'></div>
// Much of the base CSS for BB-8 is credit to this Pen by Mike Dixon: https://codepen.io/mdixondesigns/pen/PPEJwz, which was the original inspiration for *this* Pen!


*,*:before,*:after { box-sizing: border-box; }
body {
    background: #CD7640;
    overflow: hidden;
}

p {
  font-family: 'Roboto', sans-serif;
  font-weight: 100;
  color: white;
}

.config p {
  font-size: x-large;
}

.config {
  position: relative;
}

.control-wrap {
  float: left;
  margin: -15px 15px;
}

.logo img {
  position: fixed;
  top: 10px;
  right: 10px;
  width: 70px;
}

// BB-8
$d: 140px;
.bb8 {
    position: absolute;
    margin-left: -$d/2;
    width: $d;

    bottom: 15%;
    left: 0;
}
.antennas {
    position: absolute;

    transition: left .6s;

    left: 28%;

&.right {
     left: 6%;
 }
}
.antenna {
    background: #e0d2be;
    position: absolute;
    width: 2px;

&.short {
     height: 20px;

     top: -65px;
     left: 50px;
 }
&.long {
     border-top: 6px solid #020204;
     border-bottom: 6px solid #020204;
     height: 36px;

     top: -80px;
     left: 56px;
 }
}
.head {
    background-color: ghostwhite;
    border-radius: 90px 90px 25px 25px;
    -moz-border-radius: 90px 90px 25px 25px;
    -webkit-border-radius: 90px 90px 25px 25px;
    height: 63px;
    margin-left: -45px;
    overflow: hidden;
    position: absolute;
    width: 104px;
    z-index: 1;

    top: -56px;
    left: 53%;
.stripe {
    position: absolute;
    width: 100%;
}
.stripe.one {
    background: #7699B7;
    height: 7px;
    opacity: .8;
    z-index: 1;

    top: 3px;
}
.stripe.two {
    background: #CD7640;
    height: 4px;

    top: 14px;
}
.stripe.three {
    background: #999;
    height: 4px;
    opacity: .5;

    bottom: 3px;
}

.stripe.detail {
    display: flex;
    width: 200px;
    bottom: 7px;
    left: -38%;
    transition: left .6s;
}

.stripe.detail.right {
  left: 0;
}

.detail {
  height: 7px;

  &.zero {
    background-color: #CD7640;
    width: 2%;
    margin-left: 3px;
  }

  &.one {
    background-color: #CD7640;
    width: 8%;
    margin-left: 3px;
  }

  &.two {
    background-color: #CD7640;
    width: 6%;
    margin-left: 5px;
  }

  &.three {
    background-color: #CD7640;
    width: 4%;
    margin-left: 45px;
    height: 5px;
    margin-top: 2px;
  }

  &.four {
    background-color: #CD7640;
    width: 10%;
    margin-left: 4px;
  }

  &.five {
    background-color: #CD7640;
    width: 2%;
    margin-left: 3px;
  }
}

.eyes {
    display: block;
    height: 100%;
    position: absolute;
    width: 100%;

    transition: left .6s;

    left: 0;
}
.eyes.right {
    left: 36%;
}
.eye {
    border-radius: 50%;
    display: block;
    position: absolute;


&.one {
     background: #020204;
     border: 4px solid lightgray;
     height: 30px;
     width: 30px;

     top: 12px;
     left: 12%;
 }
&.one:after {
     background: white;
     border-radius: 50%;
     content: "";
     display: block;
     height: 3px;
     position: absolute;
     width: 3px;

     top: 4px;
     right: 4px;
 }

&.two {
     background-color: lightgrey;
     border: 1px solid #020204;
     height: 16px;
     width: 16px;

     top: 30px;
     left: 40%;

&:after {
     background: #020204;
     border-radius: 50%;
     content: "";
     display: block;
     height: 10px;
     position: absolute;
     width: 10px;

     top: 2px;
     left: 2px;
 }
}
}
}
.ball {
    background-color: ghostwhite;
    border-radius: 50%;
    height: $d+25;
    overflow: hidden;
    position: relative;
    width: $d+25;
}
.lines {
    border: 2px solid #B19669;
    border-radius: 50%;
    height: 400px;
    opacity: .6;
    position: absolute;
    width: 400px;

&.two {
     top: -10px;
     left: -250px;
 }
}
.ring {
    background: #CD7640;
    border-radius: 50%;
    height: 70px;
    margin-left: -35px;
    position: absolute;
    width: 70px;
&:after {
     background-color: ghostwhite;
     border-radius: 50%;
     content: "";
     display: block;
     height: 73%;
     margin-top: -36%;
     margin-left: -36%;
     position: absolute;
     width: 73%;

     top: 50%;
     left: 50%;
 }
&.one {
     margin-left: -40px;
     height: 90px;
     width: 100px;

     top: 2%;
     left: 42%;
 }
&.two {
     height: 40px;
     width: 80px;
     -ms-transform: rotate(50deg);
     -webkit-transform: rotate(50deg);
     transform: rotate(50deg);

     top: 65%;
     left: 8%;

&:after {
     top: 100%;
 }
}
&.three {
     height: 37px;
     width: 80px;
     -ms-transform: rotate(-50deg);
     -webkit-transform: rotate(-50deg);
     transform: rotate(-50deg);

     top: 68%;
     left: 84%;

&:after {
     top: 110%;
 }
}
}
.shadow {
    background: #3A271C;
    box-shadow: 5px 0 50px #3A271C;
    border-radius: 50%;
    height: $d/6;
    opacity: .25;
    position: absolute;
    width: $d;
    z-index: -1;

    left: 10px;
    bottom: -8px;
}

.instructions p {
  position: fixed;
  bottom: 10px;
  width: 100%;
  text-align: center;
}
// I've seen a few of these BB-8 animations about, so I thought I'd take a shot at building one using React as a bit of an exercise. My favorite thing to do is draw circles around him to make him do a little jig, but I'm easily amused.

class App extends React.Component {
    constructor(props){
        super(props);

        this.state = {
            droidX: 0,
            mouseX: 0,
            toTheRight: true,
            speed: 2,
            accelMod: 1
        }
    }

    // Keep track of the mouse position.
    handleMouseMove(event) {
        this.setState({
            mouseX: event.pageX
        })
    }

    // Speed Mod Bar
    handleSpeedChange(e) {
        if(parseFloat(e.target.value)) {
            this.setState({
                speed: e.target.value
            })
        }
    }

    // Acceleration Mod Bar
    handleAccelChange(e) {
        if(parseFloat(e.target.value)) {
            this.setState({
                accelMod: e.target.value
            })
        }
    }

    // Get moving!
    movement() {
        let {droidX, mouseX, speed, accelMod} = this.state;

        // Need a pretty strict if statement to make sure React doesn't end up in a 
        // render loop with all the state changes / re-rendering going on.
        if(Math.abs(Math.round(droidX)-mouseX) !== 1){
          
            let distance = mouseX - droidX;
            let acceleration = Math.abs(distance * accelMod) / 100;

            // Move to the right
            if (droidX < mouseX) {
                this.setState({
                    droidX: droidX+(speed*acceleration),
                    toTheRight: true
                });
            }
          
            // Move to the left
            else {
                this.setState({
                    droidX: droidX-(speed*acceleration),
                    toTheRight: false
                });
            }
        }
    }

    // Get some initial movement on first mount. 
    componentWillMount() {
        this.setState({
            mouseX: 300
        });
    }

    // Set up the mouse event listener and fire up the movement function.
    componentDidMount() {
        document.addEventListener('mousemove', (e) => this.handleMouseMove(e));
        setInterval(this.movement.bind(this), 1);
    }
  
    // Clean up.
    componentWillUnmount() {
        document.removeEventListener('mousemove', (e) => this.handleMouseMove(e));
    }

    // Away we go.
    render() {
      let {speed, accelMod, droidX, mouseX, toTheRight} = this.state;
      
      return (
        <div>
          
          <div className="logo">
            <img src="http://i68.tinypic.com/iod6yh.png" />
          </div>
          
          <div className="config">
            <div className='control-wrap'>
              <p>Speed: {speed}</p>
                <input
                  type="range"
                  min="0"
                  max="11"
                  step="0.1"
                  value={speed}
                  onChange={this.handleSpeedChange.bind(this)} />
            </div>
            <div className='control-wrap'>
              <p>Acceleration: {accelMod}</p>
                <input
                  type="range"
                  min="0"
                  max="3"
                  step="0.1"
                  value={accelMod}
                  onChange={this.handleAccelChange.bind(this)} />
            </div>
          </div>
          
          <div className="bb8" style={{WebkitTransform: `translateX(${droidX}px)`}}>
            <div className={'antennas ' + (toTheRight ? 'right' : '')}
                 style={{WebkitTransform: `translateX(${(mouseX - droidX) / 25}px) rotateZ(${(mouseX - droidX) / 80 }deg)`}}>
              <div className="antenna short"></div>
              <div className="antenna long"></div>
            </div>
            <div className="head"
                 style={{WebkitTransform: `translateX(${(mouseX - droidX) / 15}px) rotateZ(${(mouseX - droidX) / 25}deg)`}}>
              <div className="stripe one"></div>
              <div className="stripe two"></div>
              <div className={'eyes ' + (toTheRight ? 'right' : '')}>
                <div className="eye one"></div>
                <div className="eye two"></div>
              </div>
              <div className={'stripe detail ' + (toTheRight ? 'right' : '')}>
                <div className="detail zero"></div>
                <div className="detail zero"></div>
                <div className="detail one"></div>
                <div className="detail two"></div>
                <div className="detail three"></div>
                <div className="detail four"></div>
                <div className="detail five"></div>
                <div className="detail five"></div>
              </div>
              <div className="stripe three"></div>
            </div>
            <div className="ball" style={{WebkitTransform: `rotateZ(${droidX / 2}deg)`}}>
              <div className="lines one"></div>
              <div className="lines two"></div>
              <div className="ring one"></div>
              <div className="ring two"></div>
              <div className="ring three"></div>
            </div>
            <div className="shadow"></div>
          </div>
          
          <div className="instructions">
            <p>move your mouse.</p>
          </div>
          
        </div>
      );
    }
}

ReactDOM.render(<App />, document.getElementById('app'));
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://fb.me/react-15.1.0.min.js
  2. https://fb.me/react-dom-15.1.0.min.js