<div class="instructions">
<div class="instructions__item">Use arrows to move</div><br>
<div class="instructions__item">Hold space for rain</div><br>
</div>
<div class="post">See how it was made <a href="https://codepen.io/MarioD/post/7bc352c11f2ca67c06084fb7fb00c56e/bike-to-shool-the-process" class="post__link" target="_blank">here</a></div>
<div class="planet">
<svg class="rotating-fog" x="0px" y="0px" viewBox="0 0 295 268.5" style="enable-background:new 0 0 295 268.5;" xml:space="preserve">
<path class="st0" d="M295,160.5c0-19.2-10.4-36-26-45c-3.1-15.9-13.5-29.3-27.6-36.4c1-4,1.5-8.2,1.5-12.6c0-28.7-23.3-52-52-52
c-5.7,0-11.3,0.9-16.4,2.6C165.1,6.6,151.3,0,136,0c-19,0-35.7,10.2-44.8,25.5c-9-7.5-20.6-12-33.2-12c-28.7,0-52,23.3-52,52
c0,10.1,2.9,19.6,7.9,27.6C5.3,102.4,0,114.8,0,128.5c0,13.9,5.4,26.5,14.3,35.8C9,172.4,6,182.1,6,192.5c0,28.7,23.3,52,52,52
c2.1,0,4.2-0.1,6.2-0.4c8.4,5.9,18.7,9.4,29.8,9.4c3.9,0,7.6-0.4,11.3-1.2c9.5,10,22.9,16.2,37.7,16.2c13.7,0,26.1-5.3,35.4-13.9
c8.6,6.2,19.2,9.9,30.6,9.9c28.7,0,52-23.3,52-52c0-1.1,0-2.1-0.1-3.2C280.8,202,295,182.9,295,160.5z"/>
</svg>
<div class="path-wrapper">
<svg class="grass-1" x="0px" y="0px" viewBox="0 0 57.8 29" style="enable-background:new 0 0 57.8 29;" xml:space="preserve">
<path d="M57.8,0c0,0-25,10.5-25,29H21C21,10.5,0,5,0,5c16.8,0,21.7,6.4,26.9,14.1C32.2,11.4,41,0,57.8,0z"/>
</svg>
<svg class="grass-2" x="0px" y="0px" viewBox="0 0 57.8 29" style="enable-background:new 0 0 57.8 29;" xml:space="preserve">
<path d="M57.8,0c0,0-25,10.5-25,29H21C21,10.5,0,5,0,5c16.8,0,21.7,6.4,26.9,14.1C32.2,11.4,41,0,57.8,0z"/>
</svg>
<svg class="grass-3" x="0px" y="0px" viewBox="0 0 57.8 29" style="enable-background:new 0 0 57.8 29;" xml:space="preserve">
<path d="M57.8,0c0,0-25,10.5-25,29H21C21,10.5,0,5,0,5c16.8,0,21.7,6.4,26.9,14.1C32.2,11.4,41,0,57.8,0z"/>
</svg>
<svg class="tree-01" x="0px" y="0px" viewBox="0 0 266.7 346.7" style="enable-background:new 0 0 266.7 346.7;" xml:space="preserve">
<path d="M238.7,346.7l-17.3-77.3c9.3,11.3,45.3,20,45.3,20C240,272,204,206,204,206c8,5.3,31.3,6,31.3,6
c-27.3-14-56.7-79.3-56.7-79.3c6.7,4.7,20,2.7,20,2.7c-24-12.7-47.3-72.7-47.3-72.7c11.3,8,20.7,6.7,20.7,6.7
C148.7,51.3,133.3,0,133.3,0c0,0-15.3,51.3-38.7,69.3c0,0,9.3,1.3,20.7-6.7c0,0-23.3,60-47.3,72.7c0,0,13.3,2,20-2.7
c0,0-29.3,65.3-56.7,79.3c0,0,23.3-0.7,31.3-6c0,0-36,66-62.7,83.3c0,0,36-8.7,45.3-20L28,346.7H238.7z"/>
</svg>
<svg class="tree-02" x="0px" y="0px" viewBox="0 0 266.7 346.7" style="enable-background:new 0 0 266.7 346.7;" xml:space="preserve">
<path d="M238.7,346.7l-17.3-77.3c9.3,11.3,45.3,20,45.3,20C240,272,204,206,204,206c8,5.3,31.3,6,31.3,6
c-27.3-14-56.7-79.3-56.7-79.3c6.7,4.7,20,2.7,20,2.7c-24-12.7-47.3-72.7-47.3-72.7c11.3,8,20.7,6.7,20.7,6.7
C148.7,51.3,133.3,0,133.3,0c0,0-15.3,51.3-38.7,69.3c0,0,9.3,1.3,20.7-6.7c0,0-23.3,60-47.3,72.7c0,0,13.3,2,20-2.7
c0,0-29.3,65.3-56.7,79.3c0,0,23.3-0.7,31.3-6c0,0-36,66-62.7,83.3c0,0,36-8.7,45.3-20L28,346.7H238.7z"/>
</svg>
<svg class="tree-03" x="0px" y="0px" viewBox="0 0 266.7 346.7" style="enable-background:new 0 0 266.7 346.7;" xml:space="preserve">
<path d="M238.7,346.7l-17.3-77.3c9.3,11.3,45.3,20,45.3,20C240,272,204,206,204,206c8,5.3,31.3,6,31.3,6
c-27.3-14-56.7-79.3-56.7-79.3c6.7,4.7,20,2.7,20,2.7c-24-12.7-47.3-72.7-47.3-72.7c11.3,8,20.7,6.7,20.7,6.7
C148.7,51.3,133.3,0,133.3,0c0,0-15.3,51.3-38.7,69.3c0,0,9.3,1.3,20.7-6.7c0,0-23.3,60-47.3,72.7c0,0,13.3,2,20-2.7
c0,0-29.3,65.3-56.7,79.3c0,0,23.3-0.7,31.3-6c0,0-36,66-62.7,83.3c0,0,36-8.7,45.3-20L28,346.7H238.7z"/>
</svg>
<svg class="tree-04" x="0px" y="0px" viewBox="0 0 266.7 346.7" style="enable-background:new 0 0 266.7 346.7;" xml:space="preserve">
<path d="M238.7,346.7l-17.3-77.3c9.3,11.3,45.3,20,45.3,20C240,272,204,206,204,206c8,5.3,31.3,6,31.3,6
c-27.3-14-56.7-79.3-56.7-79.3c6.7,4.7,20,2.7,20,2.7c-24-12.7-47.3-72.7-47.3-72.7c11.3,8,20.7,6.7,20.7,6.7
C148.7,51.3,133.3,0,133.3,0c0,0-15.3,51.3-38.7,69.3c0,0,9.3,1.3,20.7-6.7c0,0-23.3,60-47.3,72.7c0,0,13.3,2,20-2.7
c0,0-29.3,65.3-56.7,79.3c0,0,23.3-0.7,31.3-6c0,0-36,66-62.7,83.3c0,0,36-8.7,45.3-20L28,346.7H238.7z"/>
</svg>
<svg class="tree-05" x="0px" y="0px" viewBox="0 0 266.7 346.7" style="enable-background:new 0 0 266.7 346.7;" xml:space="preserve">
<path d="M238.7,346.7l-17.3-77.3c9.3,11.3,45.3,20,45.3,20C240,272,204,206,204,206c8,5.3,31.3,6,31.3,6
c-27.3-14-56.7-79.3-56.7-79.3c6.7,4.7,20,2.7,20,2.7c-24-12.7-47.3-72.7-47.3-72.7c11.3,8,20.7,6.7,20.7,6.7
C148.7,51.3,133.3,0,133.3,0c0,0-15.3,51.3-38.7,69.3c0,0,9.3,1.3,20.7-6.7c0,0-23.3,60-47.3,72.7c0,0,13.3,2,20-2.7
c0,0-29.3,65.3-56.7,79.3c0,0,23.3-0.7,31.3-6c0,0-36,66-62.7,83.3c0,0,36-8.7,45.3-20L28,346.7H238.7z"/>
</svg>
<svg class="mountain-1" x="0px" y="0px" viewBox="0 0 184 184" style="enable-background:new 0 0 184 184;" xml:space="preserve">
<path d="M169.9,184H14.1C6.4,184,0,177.6,0,169.9V14.1C0,6.4,6.4,0,14.1,0h155.7c7.8,0,14.1,6.4,14.1,14.1v155.7
C184,177.6,177.6,184,169.9,184z"/>
</svg>
<svg class="mountain-2" x="0px" y="0px" viewBox="0 0 184 184" style="enable-background:new 0 0 184 184;" xml:space="preserve">
<path d="M169.9,184H14.1C6.4,184,0,177.6,0,169.9V14.1C0,6.4,6.4,0,14.1,0h155.7c7.8,0,14.1,6.4,14.1,14.1v155.7
C184,177.6,177.6,184,169.9,184z"/>
</svg>
<svg class="mountain-4" x="0px" y="0px" viewBox="0 0 184 184" style="enable-background:new 0 0 184 184;" xml:space="preserve">
<path d="M169.9,184H14.1C6.4,184,0,177.6,0,169.9V14.1C0,6.4,6.4,0,14.1,0h155.7c7.8,0,14.1,6.4,14.1,14.1v155.7
C184,177.6,177.6,184,169.9,184z"/>
</svg>
<svg class="mountain-5" x="0px" y="0px" viewBox="0 0 184 184" style="enable-background:new 0 0 184 184;" xml:space="preserve">
<path d="M169.9,184H14.1C6.4,184,0,177.6,0,169.9V14.1C0,6.4,6.4,0,14.1,0h155.7c7.8,0,14.1,6.4,14.1,14.1v155.7
C184,177.6,177.6,184,169.9,184z"/>
</svg>
<svg class="rock-1" x="0px" y="0px" viewBox="0 0 221 126" style="enable-background:new 0 0 221 126;" xml:space="preserve">
<path d="M221.2,71.5c0-23.8-19.2-43-43-43c-10.1,0-19.4,3.5-26.8,9.3c-12.3-22.1-40-37.5-72.2-37.5c-43.6,0-79,28.2-79,63l-0.3,62.3
h220.7L221.2,71.5z"/>
</svg>
<svg class="rock-2" x="0px" y="0px" viewBox="0 0 221 126" style="enable-background:new 0 0 221 126;" xml:space="preserve">
<path d="M221.2,71.5c0-23.8-19.2-43-43-43c-10.1,0-19.4,3.5-26.8,9.3c-12.3-22.1-40-37.5-72.2-37.5c-43.6,0-79,28.2-79,63l-0.3,62.3
h220.7L221.2,71.5z"/>
</svg>
<div class="bike">
<div id="bm">
<div class="light"></div>
<div class="umbrella-animation"></div>
<div class="bike__marker"></div>
</div>
</div>
<svg width="242.7" height="244.2" x="0px" y="0px" viewBox="0 0 242.7 244.2" style="enable-background:new 0 0 242.7 244.2;" xml:space="preserve">
<path class="outline" d="M3.3,120.6c0-20.4-4.5-39.8,3.6-57.2c11.9-25.6,30.5-29.8,54.9-43.7C81.6,8.3,94.5,1.9,119,1.9
c20.6,0,43,7.6,60.5,15.7c20.6,9.6,38.3,14.3,51.7,32.3c16.7,22.6,6.6,40.5,6.6,70.7c0,18.7,3.2,35.6-3.7,51.8
c-9.6,22.7-12.2,23.2-31.8,37.6c-22.5,16.5-48.3,32.3-78.3,32.3c-22,0-44.8-11.2-63.1-20.5C34.6,208.6,20.2,208,7.2,181.6
C-1.7,163.5,3.3,142.2,3.3,120.6z"/>
</svg>
<div class="cloud-boundary">
<div class="rain">
<div class="rain__drops-top"></div>
<div class="rain__drops-bottom"></div>
</div>
<div class="cloud-boundary__marker"></div>
<div class="cloud">
<svg class="cloud-svg" x="0px" y="0px" viewBox="0 0 609.5 310" style="enable-background:new 0 0 609.5 310;" xml:space="preserve">
<path d="M549.5,201c0,0-13-90-105-77c0,0-3-115-118-115c-94,0-116,84-116,84S87.7,68.4,81.1,203.2
c0,0-62.9-20.8-71.6,45.8c-7,53.5,68,52,68,52l482-5c0,0,41-1.7,41-45.5C600.5,218,577.8,201,549.5,201z"/>
</svg>
</div>
</div>
</div>
</div>
* {
box-sizing: border-box;
}
html {
height: 100%;
}
body {
margin: 0;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
overflow: hidden;
background: #b5c755;
background: radial-gradient(ellipse at center, #b5c755 0%, #8fb467 50%);
}
.post {
position: absolute;
bottom: 20px;
left: 20px;
font-family: 'Source Sans Pro', sans-serif;
color: rgba(0, 0, 0, 0.4);
font-size: 12px;
}
.post__link {
color: rgba(0, 0, 0, 0.6);
}
.planet {
width: 300px;
height: 300px;
border-radius: 100%;
position: relative;
}
.path-wrapper {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 50;
}
.outline {
fill: transparent;
stroke: #4d3034;
fill: #82b765;
stroke-width: 2;
position: relative;
z-index: 5;
}
/* ----- */
/* Cloud */
/* ----- */
.cloud-boundary {
width: 100px;
height: 270px;
position: absolute;
top: calc(-50% - 30px);
left: 50%;
transform: translateX(-50%);
transform-origin: bottom center;
z-index: -2;
will-change: auto;
}
.cloud-boundary__marker {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 1px;
height: 1px;
}
.cloud {
width: 100%;
height: 50px;
position: absolute;
top: 0;
left: 0;
}
.cloud-svg {
fill: #ffffff;
}
/* ---- */
/* Rain */
/* ---- */
.rain {
width: calc(100% - 20px);
height: 100%;
position: absolute;
top: 40px;
left: 50%;
transform: translateX(-50%);
overflow: hidden;
transition: opacity 0.3s;
opacity: 0;
}
.rain--active {
opacity: 0.6;
}
.rain__drops-top {
height: 100%;
width: 100%;
position: absolute;
left: 0;
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/rain.png) 0 0 no-repeat;
background-size: 100% 100%;
opacity: 1;
}
.rain__drops-bottom {
height: 100%;
width: 100%;
position: absolute;
left: 0;
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/rain.png) 0 0 no-repeat;
background-size: 100% 100%;
opacity: 1;
}
/* ---- */
/* Bike */
/* ---- */
.bike {
position: absolute;
top: -40px;
left: -20px;
width: 40px;
height: 80px;
z-index: -2;
transform: rotate(0);
will-change: auto;
}
.bike__marker {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 1px;
height: 1px;
}
#bm {
position: relative;
}
.bike svg {
z-index: 20;
position: relative;
will-change: auto;
}
.umbrella-animation {
position: absolute;
top: -12px;
left: calc(50% + 2px);
transform: translateX(-50%);
width: 100%;
z-index: 10;
}
.light {
position: absolute;
top: 18px;
left: 30px;
width: 0;
z-index: 10;
height: 10px;
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/light.png) 0 0 no-repeat;
background-size: 22px 10px;
transition: width 0s 0.3s, opacity 0.3s;
transform-origin: center left;
opacity: 0;
}
.light--active {
width: 22px;
transition: width 0.2s, opacity 0s;
opacity: 0.6;
}
/* ------- */
/* Scenery */
/* ------- */
.tree-01 {
width: 40px;
position: absolute;
top: 5px;
left: -5px;
transform: rotate(-41deg);
z-index: -3;
fill: #1f6219;
}
.tree-02 {
width: 30px;
position: absolute;
top: 6px;
left: 16px;
transform: rotate(-41deg);
z-index: -1;
fill: #1a5215;
}
.tree-03 {
width: 40px;
position: absolute;
top: 225px;
left: 75px;
transform: rotate(-161deg);
z-index: -3;
fill: #1f6219;
}
.tree-04 {
width: 30px;
position: absolute;
top: 225px;
left: 98px;
transform: rotate(-169deg);
z-index: -1;
fill: #1a5215;
}
.tree-05 {
width: 30px;
position: absolute;
top: 24px;
left: 225px;
transform: rotate(-304deg);
z-index: -1;
fill: #1a5215;
}
.mountain-1 {
fill: #97b45c;
position: absolute;
top: -18px;
left: 80px;
transform: rotate(40deg);
width: 60px;
z-index: -5;
}
.mountain-2 {
fill: #97b45c;
position: absolute;
top: -5px;
left: 119px;
transform: rotate(56deg);
width: 60px;
z-index: -5;
}
.mountain-4 {
fill: #97b45c;
position: absolute;
top: 156px;
left: 182px;
transform: rotate(77deg);
width: 60px;
z-index: -5;
}
.mountain-5 {
fill: #97b45c;
position: absolute;
top: 180px;
left: 159px;
transform: rotate(10deg);
width: 60px;
z-index: -5;
}
.rock-1 {
position: absolute;
top: 127px;
left: -8px;
width: 20px;
transform: rotate(-90deg);
z-index: -1;
fill: #6a6a6a;
}
.rock-2 {
position: absolute;
top: 199px;
left: 198px;
width: 20px;
transform: rotate(-206deg);
z-index: -1;
fill: #6a6a6a;
}
.rotating-fog {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
fill: #fff;
opacity: 0.05;
width: 92%;
height: 92%;
animation: spin;
animation-duration: 40s;
animation-timing-function: linear;
animation-iteration-count: infinite;
z-index: -10;
}
@keyframes spin {
0% {transform: translate(-50%, -50%) rotate(360deg);}
100% {transform: translate(-50%, -50%) rotate(0deg);}
}
.grass-1 {
position: absolute;
top: 100px;
left: -4px;
transform: rotate(-86deg);
width: 10px;
height: 20px;
fill: #4d3034;
z-index: -10;
}
.grass-2 {
position: absolute;
top: 209px;
left: 46px;
transform: rotate(-156deg);
width: 10px;
height: 20px;
fill: #4d3034;
z-index: -10;
}
.grass-3 {
position: absolute;
top: 109px;
left: 235px;
transform: rotate(-266deg);
width: 10px;
height: 20px;
fill: #4d3034;
z-index: -10;
}
/* ------------ */
/* Instructions */
/* ------------ */
.instructions {
position: absolute;
top: 0;
left: 0;
padding: 20px;
}
.instructions__item {
font-family: 'Source Sans Pro', sans-serif;
color: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 3px;
padding: 4px 10px;
display: inline-block;
margin-top: 10px;
font-size: 12px;
}
.instructions__item:first-child {
margin-top: 0;
}
const cloudBoundary = document.querySelector('.cloud-boundary');
const bike = document.querySelector('.bike');
const bmContainer = document.querySelector('#bm');
const umbrellaAnimation = document.querySelector('.umbrella-animation');
const rain = document.querySelector('.rain');
const rainDropsTop = document.querySelector('.rain__drops-top');
let rainDropsTopPosition = rain.clientHeight * -1;
const rainDropsBottom = document.querySelector('.rain__drops-bottom');
let rainDropsBottomPosition = 0;
let space;
const light = document.querySelector('.light');
// ------------------------
// Bodymovin bike animation
// ------------------------
var animation = bodymovin.loadAnimation({
container: bmContainer,
renderer: 'svg',
loop: true,
autoplay: true,
path: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/bike.json',
});
animation.setSpeed(1);
var umbrella = bodymovin.loadAnimation({
container: umbrellaAnimation,
renderer: 'svg',
loop: false,
autoplay: false,
path: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/49240/umbrella.json',
});
umbrella.setSpeed(2);
// ------------------------------
// Anime.js animation around path
// ------------------------------
var path = anime.path('.outline');
var motionPath = anime({
targets: '.bike',
translateX: path('x'),
translateY: path('y'),
rotate: path('angle'),
easing: 'linear',
duration: 15000,
loop: true
});
// -----------------------------------------------
// Detect collision between the bike and the cloud
// -----------------------------------------------
const bikeMarker = document.querySelector('.bike__marker');
const cloudBoundaryMarker = document.querySelector('.cloud-boundary__marker');
const detectCollision = function(item1, item2) {
return Math.sqrt((item2.left - item1.left) * (item2.left - item1.left) + (item2.top - item1.top) * (item2.top - item1.top));
};
// -------------------------------
// Keyboard controls for the cloud
// -------------------------------
let cloudBoundaryAngle = 0;
let cloudBoundaryVelocity = 0;
let left = false;
let right = false;
let lastDirection = '';
document.addEventListener('keydown', (e) => {
if(e.keyCode === 37) {
left = true;
lastDirection = 'left';
} else if (e.keyCode === 39) {
left = true;
lastDirection = 'right';
} else if (e.keyCode === 32) {
space = true;
}
});
document.addEventListener('keyup', (e) => {
if(e.keyCode === 37) {
left = false;
} else if (e.keyCode === 39) {
left = false;
} else if (e.keyCode === 32) {
space = false;
}
});
const step = function() {
// --------------
// Cloud movement
// --------------
if(left || right) {
if (cloudBoundaryVelocity < 2) {
cloudBoundaryVelocity += 0.2;
}
}
cloudBoundary.style.transform = `translateX(-50%) rotate(${cloudBoundaryAngle}deg)`
if(cloudBoundaryVelocity > 0) {
if(lastDirection === 'left') {
cloudBoundaryAngle -= cloudBoundaryVelocity;
} else if (lastDirection === 'right') {
cloudBoundaryAngle += cloudBoundaryVelocity;
}
cloudBoundaryVelocity -= 0.05;
} else {
cloudBoundaryVelocity = 0;
}
// ------------------------------------
// Trigger animations based on distance
// ------------------------------------
let distanceFromCloud = detectCollision(bikeMarker.getBoundingClientRect(), cloudBoundaryMarker.getBoundingClientRect());
if (distanceFromCloud < 60) {
light.classList.add('light--active');
} else {
if (light.classList.contains('light--active')) {
light.classList.remove('light--active');
}
}
if (distanceFromCloud < 80 && space) {
if(!bmContainer.classList.contains('active')) {
bmContainer.classList.add('active');
umbrella.setDirection(1);
umbrella.play();
}
} else {
if(bmContainer.classList.contains('active')) {
bmContainer.classList.remove('active');
umbrella.setDirection(-1);
umbrella.play();
}
}
// -------------------
// Rain on space press
// -------------------
if (space) {
if(!rain.classList.contains('rain--active')) {
rain.classList.add('rain--active');
}
} else {
if(rain.classList.contains('rain--active')) {
rain.classList.remove('rain--active');
}
}
rainDropsTop.style.top = `${rainDropsTopPosition}px`;
rainDropsBottom.style.top = `${rainDropsBottomPosition}px`;
rainDropsTopPosition += 3;
rainDropsBottomPosition += 3;
if (rainDropsTopPosition >= 0) {
rainDropsTopPosition = rain.clientHeight * -1;
}
if (rainDropsBottomPosition >= rain.clientHeight) {
rainDropsBottomPosition = 0;
}
window.requestAnimationFrame(step);
};
window.requestAnimationFrame(step);
View Compiled
This Pen doesn't use any external CSS resources.