Pure CSS: Bicyclist Animation

Cycling

See Codepen

My first pure CSS animation! A loose HTML and CSS translation of Dribble shot “Cycling” by Moncho Massé. I say ‘loose’ because it wasn’t my original intention to do a graphic art to HTML/CSS translation — just wanted to use the color palette (which is so warm and gorgeous). Eventually decided to use the original work as replication practice. Based the wheels off of this flat illustration:

white bike

Process:

  1. Bike
  2. Bike Animation
  3. Girl
  4. Girl Animation

1. Bike

I started off with the bike, so that that the girl could later sit on something visible. The stationary but spinning wheels were also a great deal less intimidating to start off with than the moving legs.

a) Wheel
The wheel was easy to make — a circle with background: none , with a border that acted as the tire, and box-shadow: inset as the inner lining of the tire.

CSS Syntax

box-shadow:  h-shadow    v-shadow    blur    spread-color

Things to note about box-shadow:

  • when all values are 0px, the shadow cannot be seen because it is directly behind its element
  • if the element has a border, the box-shadow is contained within this space, regardless of h-shadow or v-shadow values

NEW: Sass

So I learned the basics of Sass with Dan Cederholm’s “Sass for Web Designers,” and implemented what I learned for the first time in this project. Seeing that two wheels are needed, I created a mixin for the properties that both wheel elements  would share.

cycling_mixin_wheel

That left the class selectors ‘back-wheel’ and ‘front-wheel’, whose only difference was the value of the left position property.

mixin-back-front-wheels

At the time I was just excited to use my newly acquired knowledge of Sass. Looking back at this, I would not have used @mixin.

The above SCSS code would have compiled to the following in CSS:

.wheel {
  position: absolute; 
  border-radius:50%; 
  width: 200px; 
  height: 200px; 
  background:black; 
  border: 12px solid $wheel;
  box-shadow: inset 20px 500px 0px 10px $wheel-lining;
  top: 360px; 
  z-index: 2;
}

.back-wheel {
  position: absolute; 
  border-radius:50%; 
  width: 200px; 
  height: 200px; 
  background:black; 
  border: 12px solid $wheel;
  box-shadow: inset 20px 500px 0px 10px $wheel-lining;
  top: 360px; 
  z-index: 2;
  left: 20px; 
}

.front-wheel {
  position: absolute; 
  border-radius:50%; 
  width: 200px; 
  height: 200px; 
  background:black; 
  border: 12px solid $wheel;
  box-shadow: inset 20px 500px 0px 10px $wheel-lining;
  top: 360px; 
  z-index: 2;
  left: 440px; 
}

This generated CSS is not following the DRY principle. If mixins are used, the styles in them are duplicated all over the classes.

Alternative methods:
1) I could have used another class named ‘wheel’ in place of the mixin:

HTML

cycling-html-test

SCSS

.wheel {
  position: absolute; 
  border-radius:50%; 
  width: 200px; 
  height: 200px; 
  background:black; 
  border: 12px solid $wheel;
  box-shadow: inset 20px 500px 0px 10px $wheel-lining;
  top: 360px; 
  z-index: 2;
}

.back-wheel {
  left: 20px; 
}

.front-wheel {
  left: 440px; 
}

2) I could have also used the @extend directive to share CSS properties between these selectors:

HTML

cycling-html-test

SCSS

.wheel {
  position: absolute; 
  border-radius:50%; 
  width: 200px; 
  height: 200px; 
  background:black; 
  border: 12px solid $wheel;
  box-shadow: inset 20px 500px 0px 10px $wheel-lining;
  top: 360px; 
  z-index: 2;
}

.back-wheel {
  @extend .wheel; 
  left: 20px; 
}

.front-wheel {|
  @extend .wheel; 
  left: 440px; 
}

Which compiles to the following in CSS:

.wheel, .back-wheel, .front-wheel {
  position: absolute; 
  border-radius:50%; 
  width: 200px; 
  height: 200px; 
  background:black; 
  border: 12px solid $wheel;
  box-shadow: inset 20px 500px 0px 10px $wheel-lining;
  top: 360px; 
  z-index: 2;
}

.back-wheel {
  left: 20px; 
}

.front-wheel {
  left: 440px; 
}

Which brings us to the question — is alternative method #1 or #2 better in this scenario?

Oh wait, newly in: 3) Using Sass placeholder selectors with the @extend directive:

%wheel {
  position: absolute; 
  border-radius:50%; 
  width: 200px; 
  height: 200px; 
  background:black; 
  border: 12px solid $wheel;
  box-shadow: inset 20px 500px 0px 10px $wheel-lining;
  top: 360px; 
  z-index: 2;
}

.back-wheel {
  @extend %wheel; 
  left: 20px; 
}

.front-wheel {
  @extend %wheel;
  left: 440px; 
}

Which compiles to the following in CSS:

.back-wheel, .front-wheel {
  position: absolute; 
  border-radius:50%; 
  width: 200px; 
  height: 200px; 
  background:black; 
  border: 12px solid $wheel;
  box-shadow: inset 20px 500px 0px 10px $wheel-lining;
  top: 360px; 
  z-index: 2;
}

.back-wheel {
  left: 20px; 
}

.front-wheel {
  left: 440px; 
}

The HTML for the above code is just this:

cycling-html-test2

Therefore, when looking at the generated CSS, alternative method #3) using Sass placeholder selector with @extend directive:

  1. Simplifies CSS selectors
  2. Keep properties DRY
  3. Reduce the class attributes used in HTML

Which seems like the best method for better maintainability of the code.

With regards to better overall performance of CSS (and thus the web page?) — that, I am unsure of. Intuitively I’d yes, there could be a positive effect on performance, but I’m not knowledgeable enough yet to say how and why that would be so. {CHECK} 

b) Spokes 

Modeled the spokes orientation after the numbers on a clock-face. So the class named ‘_12-6’ is the vertical spoke right in the center of the wheel. Needed 6 spokes in total.

As for spacing between spokes:  360 degrees (of a circle) divided by 12 (hours on a clock) equals 30 degrees (between each hour on the clock-face).

Used the @include directive to reference @mixin rotate($deg). I almost reached the same mixin, but was missing the # symbol and {} braces. The generic transform mixin I found online.

cycling_spokescycling_mixin_rotate

The property background: none gave the illusion of seeing through the spokes of the wheel to the background.

c) Bike body frame, handlebar, seat, hub, chains, crankset, crank

Just rotating and positioning into place.

2. Bike Animation

a) Cassette & Spokes

I made the cassette (cluster of sprockets on the rear hub) the parent div of all the spokes divs. This way, all I need to rotate is the cassette itself, to give the impression of spinning wheels.

The transform-origin of the cassette is the default 50% 50%, smack-dab in its center, where the spokes’ convergence point is fixed.

Used a mixin for the @keyframes rule (found online) to specify the animation code.

cycling_generic_animation

{CHECK} Why the # symbol before the animation-name and str?

cycling_cassette_rotation

NOTE: Used the value 359 and not 360 because degrees in rotate() can only take values in this range: [0, 360).

To go clockwise (because the bicycle is supposed to be moving from left to right), a negative value (-359 degrees) was used as the starting property value.

Animation shorthand – CSS Syntax:

animation: name duration timing-function delay iteration-count direction fill-mode play-state;

cycling_cassette

In order, here I used:

  • rotation –> animation-name (specifies the name of the keyframe to bind to the selector)
  • 1s –> animation-duration (specifies how many seconds an animation takes to complete)
  • infinite –> animation-iteration-count (specifies how many times an animation should be played)
  • linear –> animation-timing-function (specifies the speed curve of the animation); linear specifies an animation with the same speed from start to end

So the order in the given syntax doesn’t matter, unless when using both duration and delay.

b) Crank 

Had some trouble with transform-origin initially. For some reason, the crank was rotating somewhere way above the crankset. Re-positioned and set transform-origin to ‘bottom center’. Could have done ‘bottom left’, and the result would be the same.

In the beginning, I made the pedal a child div of crank, and had it rotating with the crank. But the pedal was supposed to be fixed in its original position while rotating. In later iterations, the pedal became a child div of shoe, which allowed it to keep its original orientation while rotating. Yes, a hack, I know.

3. Girl

Pelvic region
Started off with the pelvic region, so that there was something digitally-tangible sitting on top of the bike seat.

Face 
Would like to point out that the eyes have background: none, but box-shadow: inset so the curved eye effect exists. Without inset, the curve would be flipped to the bottom of the no-background element.

Torso
Made the right arm div a child element of the chest div. Wanted to make the left arm a child element of chest div as well, but it needed to be behind the chest. A child element would always appear above the parent element, so I had to make a separate div for the left arm, give it a lower z-index than the chest div, and position it appropriately.

In retrospect, should have made the belly div a child element of chest as well. Same goes for neck (either a child element of chest or head).

Making child elements like this will make it easier to animate — just animate the parent div to animate the children divs too.

Legs
Similarly, the calves became child elements of the thighs.

4. Girl Animation

The most time-consuming task of all.

After eyeballing the original animation for a very long time, motion could be broken down into:

a) Thighs

Rotating about ~40-60 degrees continuously, at alternating times to each other. The fixed point of rotation (transform-origin) is on the ‘pelvic region’ div. The keyframes values were eventually settled through a lot of trial-and-error.

b) Calves 

In the beginning, the calves were not moving on their own, just using motion from the thighs.

After a lot of testing, the movement for the right calf was deconstructed to four parts: starting at the ‘rest’ position, followed by a decrease in degrees (to rotate counter-clockwise; to kick out), the ‘rest’ position again, and  an increase in degrees (to rotate clockwise; to kick back).

Again, plenty of trial-and-error and fiddling around with the degree values.

Still haven’t gotten a perfect circular movement.

c) Pelvic region, torso, neck, head

Issue: Multiple transforms i.e. left arm, neck, belly, were all rotated prior to translation. Applying translation after the rotation made these elements revert back to their original orientation before rotation.

For example, I could not do this:

.l-arm {
  @include rotate(20); 
  @include transform(2, 3); 
}

When there are multiple transform directives, only the last one will be applied.

Without mixins, multiple transforms could be separated with commas i.e.

.l-arm {
transform: translate(1, 2), rotate(50deg); 
}

Couldn’t find an already existing mixin for multiple transforms that worked (and couldn’t develop one that worked) YET; for the sake of saving time, resorted to this:

cycling-move-neck

An eyesore, but functional.

d) Hair, ground streaks, wind 

Used transform: translation. Need to make smoother. Use transitions?

New things learned

  • On my search for dealing with multiple transforms and animations, I discovered that behind all transforms are browser-translated matrices! Yay linear algebra! Need to read further.
  • Used Sass (SCSS syntax) for the first time — variables, mixins, @include directive, @extend directive, placeholder selectors. Really basic. Continue learning more about Sass.
  • CSS3 animations

 

To be improved

  • Circular motion of calves — still not a perfect 360 degree rotation
  • Make the crank in full — connect the crank to the pedal for both feet
  • Position the calf on the knee so that when animating, no bumps are seen on the knees
  • Smoother animation for wind movement — transitions?
  • Create and/or find multiple transforms and multiple animations mixins
  • Closer look at keyframes timelines
  • Keep track of z-indices

 

TBC.

 

 

 

 

Pure CSS Art: Days 9 & 10 – Cabin in the Woods

Attempted to recreate Justin Middendorp’s graphic illustration (“Cabin in the Woods”), which could be found here.

Day 9: 

CabinInTheWoods1

Day 10:

Added to the clouds.

CabinInTheWoods for WordPress

See Codepen

This is far from complete. I still have to:

  • Adjust the opacity of some connecting cloud parts for a better blend
  • Add stars
  • Add texture to the cabin exterior
  • Taper the trees
  • Get rid of the outlines of the borders of the triangle shapes that make up the tips of the grey trees/bushes
  • Adjust the gradient of the sky background
  • Improve the clouds

Not going to lie, halfway through this, I stopped trying to replicate Middendorp’s work down to the little details. But once that list of items above is completed, I might go back and try to get it closer to the original work again.

 

Takeaways: 

  • Plan the number of layers for the z-indexes, and make a separate note section in HTML to keep track of them (used 8 layers for this work)
  • Used triangles for the tips of the grey trees/bushes — have to set the width and height to 0 pixels and use border-bottom as the triangle (border-left and border-right are supposed to be transparent)
  • Used the transform: rotate(#deg) property to rotate the roofs onto the cabin
  • While this was fun and immensely satisfying to put together, there was a lot of repetition (creating and moving shapes). I should have focused more on finding creative solutions to problems (see the list above) and  learning new CSS properties and tricks

 

For the future: 

  • Find a way to replicate HTML elements efficiently i.e stars
  • I made a lot of div elements with class=”star”, and then gave them each an unique id i.e. id=”star1″, id=”star2″ ; the CSS declaration for star contained properties that gave color and size to the elements; the id declarations gave position (top and left); but nothing was displayed. Why?
  • Can I create the cabin exterior’s texture with table cells with borders or outlines that are dashed?
  • Transparent borders didn’t really turn out transparent; find out why it didn’t work as expected

 

 

Pure CSS Art: Day 11

icecreamPureCSSArt

See Codepen 

I rushed this one, having started it around 11:30PM and trying to finish it before midnight. (That didn’t happen until 1AM. 1.5 hours haha. I feel like I’m regressing.)

Takeaways:

  • Used box-shadow property to create the mouth — the mouth div itself is pink, and the black shadow rests right underneath it

icecreamPureCSSArt box-shadow

  • Created a triangle shape for the cone — this involved setting both the width and height to 0 pixels, the border-left and border-right to transparent (which actually didn’t turn transparent, so I set the border colors to the background colors instead), and border-top to a color of my choosing

icecreamPureCSSArt triangle

  • The shimmer of light in each eye was created with two classes of the same name, and needed only one CSS declaration

 

Questions:

  • Why did setting the box div ‘s width and height to percentages not display anything? Using pixels does display the block
  • Same thing happened with the slants on the cone. Why?
  • Why did setting the triangle’s border transparent not actually render it transparent?

 

For the future: 

  • Add different colors or sprinkles to the ice cream

Pure CSS Art: Day 6

Days 4 and 5 – started attempting pixel art and encountered some issues (still unresolved). Stumbled upon the mention of tables, and tried that instead:

colored_table_cells_pure_css_art
Does this count as pixel art? 0.0

See Codepen 

Takeaways: 

  • <table> element makes tables out of grids (two-axes)
  • <tr> element makes rows
  • one or more <td> element is nested inside <tr> to make the cells

Example: 

<table>
  <tr> 
    <td></td> 
   <td></td> 
   <td></td> 
  </tr> 
</table>

This table has 1 row with 3 cells.

  • there is the <table> element, and the table selector; no need to make a class for table to style it
  • Sass is a CSS pre-processor (I started to use Sass but Codepen kept freezing; will try Sass again another time)
  • The cells are all empty, so the cells are thin slices of color
  • When there is content, the cells stretch to accommodate the content

Example: 

colored_table_cells_pure_css_art_TEST
Yes, the third word is the longest word in the German language, meaning “law delegating beef label monitoring”

 

For the future: 

  • Find a more concise way to create a lot of cells
  • Try Sass properly
  • Find out why the top property had no effect on the element table