[009.3] Build your own 8-bit character using Sass part IV

Automating character animation.

Subscribe now

Build your own 8-bit character using Sass part IV [07.09.2018]

Yesterday we animated our character, but what if we have more than one character? That would mean we’d have to define a lot of properties, because every character is different. That's no good, so let's use some Sass magic to automate that process. The first thing we're going to do is change the name of the class to pixel-character, and we'll use the name of each character as a modifier; for example, -adam:


<div class="pixel-character -adam">
  <div class="boots-shadow"></div>
</div>

Then we change the name of our character variable to the name of the character itself (in this case adam) and do the same with the file. Now, we create a map called characters, which will contain all of our character names.


// Characters

$characters: (
  'adam',
  'josh',
  'franze',
  'matt',
  'moises',
  'alex'
);

Now we go back to our _tools.scss and create a new mixin named setCharacter. This will have two parameters: the map of names, and a map with all the characters we have. Then we iterate through the characters map using the directive @each, select each character, and assign it to a variable called $draw. After that we use another @each to verify if some of the names we're passing exist in the current character maps.


@mixin setCharacter($names, $characters) {
  $ping: 1;
  @each $character in $characters {
    $draw: nth($characters, $ping);
    @each $name in $names {
      @if map_has_key($draw, $name) {
      }
    }
  }
}

If the name exists, we create two variables. First, $character receives the color map of the matching name; second,$length receives the total length of the character map, which we're going to use to set the height and the width.


@mixin setCharacter($names, $characters) {
  $ping: 1;
  @each $character in $characters {
    $draw: nth($characters, $ping);
    @each $name in $names {
      @if map_has_key($draw, $name) {
        $character: map-get($draw, $name);
        $length: length($character);
        &.-#{$name} {
          height: $pixel-size*$length + 2.5rem;
          width: $pixel-size*$length;
          &:after {
            animation: salute-#{$name} 6s 3s infinite alternate;
            box-shadow: build_character($character, $pixel-size);
          }
          > .boots-shadow {
            top: $pixel-size*$length;
          }
        }
        @at-root .character-container.-#{$name} {
          height: $pixel-size*$length + 8rem;
          width: $pixel-size*$length + 3rem;
        }
      }
    }
    $ping: $ping + 1;
  }
}

Then we create a modifier with the format -character_name, and define the specific properties and values for each modifier. After that, we increment the control variable, and repeat the process with the next character map. Notice that we use the directive @at-root which allow us to “jump out” of our nesting. Also notice how we're setting the animation; we do it like this because we're going to automate our animations, so we can generate the ones we need for each character.


// Animations

@mixin createAnimations($names, $characters) {
  $ping: 1;
  @each $character in $characters {
    $draw: nth($characters, $ping);
    $name: nth($names, $ping);
    @if map_has_key($draw, $name) {
      $name_peace: '' + $name + 'Salute';
      @keyframes salute-#{$name} {
        0%, 45% {box-shadow: build_character(map-get($draw, $name), $pixel-size);}
        50%, 100% { box-shadow: build_character(map-get($draw, unquote($name_peace)), $pixel-size); }
      }
    }
    $ping: $ping + 1;
  }
}

This is another way of doing the same process we just did with the characters mixin. The difference is that we're taking advantage of the fact that we know the quantity of characters available, so we can pick them in order using the control variable $ping. This will be faster and lighter than the previous logic; however, we need to have the same order for the map of names and characters, which makes it harder to maintain. Instead, the @each logic verifies all the names for each map, so it doesn’t matter what the order is or if we add new names.

Even though the logic from both mixins is different, they basically do the same thing: receive the map of names and characters, then iterate through each character and select them. Once we have one character selected, they verify if the name we're passing matches one of the color maps of the character. If it exists, then we create an animation with the format salute-name_of_character and apply the respective values.

Now we can use our mixin to generate all the animations we need:


@mixin createAnimations($names, $characters) {
  ...
}

@include createAnimations($characters, ($adam, $josh, $franze, $matt, $moises, $alex));

And now we can use our character mixin in our component pixel-character to generate all the modifiers:


.pixel-character {
  ...
  @include setCharacter($characters, ($adam, $josh, $franze, $matt, $moises, $alex));
}

Last, we add the character maps to our characters folder and modify our index.html, adding all the characters and deleting every old mixin application:


<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="assets/application.css">
  <title>Sass Features</title>
</head>
<body>
  <div class="character-container -adam">
    <h1 class="title">Pixel Adam</h1>
    <div class="pixel-character -adam">
      <div class="boots-shadow"></div>
    </div>
  </div>
  <div class="character-container -josh">
    <h1 class="title">Pixel Josh</h1>
    <div class="pixel-character -josh">
      <div class="boots-shadow"></div>
    </div>
  </div>
  <div class="character-container -franze">
    <h1 class="title">Pixel Franzé</h1>
    <div class="pixel-character -franze">
      <div class="boots-shadow"></div>
    </div>
  </div>
  <div class="character-container -matt">
    <h1 class="title">Pixel Matt</h1>
    <div class="pixel-character -matt">
      <div class="boots-shadow"></div>
    </div>
  </div>
  <div class="character-container -moises">
    <h1 class="title">Pixel Moises</h1>
    <div class="pixel-character -moises">
      <div class="boots-shadow"></div>
    </div>
  </div>
  <div class="character-container -alex">
    <h1 class="title">Pixel Alex</h1>
    <div class="pixel-character -alex">
      <div class="boots-shadow"></div>
    </div>
  </div>
</body>
</html>

And boom! We have all our characters animated. Let's center them and reduce the size, so all of them can be on the same line. Let’s also move the earthquake to the body, instead of the character containers.

// --assets/application.scss

body {
  @include flexbox('t', 'center');
  animation: earthquake .5s forwards;
  animation-delay: 3.4s;
}

// --assets/_variables.scss

$pixel-size: .645rem !default;

And now all DailyDrip team members salute you!

Summary

Well, this was an extremely fun practice. We covered complex functions, list, arglist, animations, mixins, loops, conditionals, methods — basically all of the features in Sass. Now it’s up to you to find news ways to use this tool and do crazy things like this! Below you can find some useful links; hope you enjoyed the video. See ya!

Sass Official Documentation Understanding Sass List 10 useful mixins for automation