<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;
}
View Compiled
// 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
This Pen doesn't use any external CSS resources.