Having fun with CSS
Tags: beginners, tutorial, css, javascript
Published: 2022-10-10 17:48:18

As I enter the final phase of software engineering boot camp I was reminded by a classmate that coding can be fun. So before we have to start our capstone project and polish our resumes and refine our portfolio and… I decided to have some fun coding during this long weekend. 

What better way to have fun than doing a CSS heavy project, right?  Okay maybe not.  If my cohort is a fair representation, CSS is the bane of many beginning front end developers so I decided to bring an old friend, Pitfall Harry (from Activison's 1982 game Pitfall!).

This post is going to show you how to code out an 8-bit style character and animate it’s movement using JavaScript, Html and CSS. There are three different animation types happening in the clip below and I will show how they are used in conjunction with each other. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pn62l34wjsxtmq2gsepb.GIF)

Animate a Sprite using CSS
Animate Element movement using JavaScript
Animate Element movement using CSS
 


## Sprite Animation in CSS


![Pitfall Harry running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fz2vpd9kdlb6d4suzqut.gif)

At the start we are going to need a sprite sheet.  To create this I started with an internet search and found one for Pitfall Harry that was a little messy, so I used Piskel to clean it up to get what I needed.



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uhkgvbzqqskumkwoe8qc.png)

Here is the final 5 frame sprite output from Piskel.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/joytff3eyqssv614vzvb.png)

I created one more sprite, a single frame of the character standing.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v3mgumsrx6pucrwa2w57.png)

Now that we have our sprites we need to create 3 files, index.html, script.js and style.css.

The html file looks like this:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Jump Guy</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="game">
        <div id="character"></div>
    </div>
<script src="script.js"></script>
</body>
</html>
```

We are defining two elements here, a **game** div that serves as our canvas and a **character** that our sprite will embody.



Now we will add some CSS styling to our elements. We are disabling the scroll bar in our **body** element, setting the dimensions of our **game** area and setting the size of our **character** as well as it's starting position (because our character is a child of the game element, we are setting it's position as relative to  it's parent).

```css
/* style.css */

body{
    -ms-overflow-style: none; /* for Internet Explorer, Edge */
    scrollbar-width: none; /* for Firefox */
    overflow-x: hidden; 
}

#game{
    width: 1000px;
    height: 600px;
    border: 1px solid black;
    margin:0%;
}

#character{
    width: 128px;
    height: 170px;
    position: relative;
    top: 400px; 
    scale:.75;
    left:0px;
}
```

Next we are adding links to our two sprite image files and the bulk of the code required to create the running animation.  We are letting CSS know that one of our frames is 128 pixels wide and our 5 frame sprite sheet is 640 pixels wide.  We are defining classes here that we will attach and remove as required in our JavaScript file in the next steps.

```css
/* style.css */

.background-run{
    background: url("./harry-run.png");
}

.background-stand{
    background: url("./harry-stand.png");
}

@keyframes sprite-run {
    from { background-position: 0px; }
    to { background-position: -640px; }
}

.run{
    animation: sprite-run 500ms linear;
    animation-timing-function: steps(5);
    animation-iteration-count: infinite;
}

.flip{
    transform: scaleX(-1);
}
```

In our script.js file we are getting our **character** element and attaching our **stand** sprite to it.  Then we are setting some global variables as well as the event listeners to handle our keystroke inputs.

```javascript
var character = document.getElementById("character");
character.classList.add("background-stand")

/* Global Variables */
let pressed =false
let characterLeft=0


/* Event Listeners */
document.addEventListener("keydown",keydown);
document.addEventListener("keyup",keyup)


/* Functions */
function keydown(e){
    pressed===true
    if(e.code==="ArrowRight")run(1)
    if(e.code==="ArrowLeft")run(-1)
    if(e.code==="Space")jump()
}

function keyup(e){
    if(e.code==="ArrowRight"){
        clearInterval(movetimer)
        character.classList.remove("run")
        character.classList.remove("background-run")
         character.classList.add("background-stand")
        pressed=false
    }

    if(e.code==="ArrowLeft"){
        clearInterval(movetimer)
        character.classList.remove("run")
        character.classList.remove("background-run")
        character.classList.add("background-stand")
        pressed=false
    }
}

```

Next we define our **run** function taking our **direction** parameter and using it to tell the character in which direction to face based off of our arrow key inputs.

```javascript
function run(direction){
    if(pressed===true){return;}

    /* Setting the direction of the character */
    direction<0 ? (character.classList.add("flip")) : (character.classList.remove("flip"))

    character.classList.remove("background-stand")
    character.classList.add("background-run")
    character.classList.add("run")
    pressed=true
}
```

At this point when our page loads our character is standing in the bottom left corner of our game area and he will run in place if you press and hold the right or left arrow key as well as face the correct direction.

## Element Animation in JavaScript

What he won't do is actually run anywhere.  We will remedy this by adding a few things to our script.js file.

Add this to our global variables: `let moveTimer = 0`

At the end of our **run** function add: 
`moveTimer=setInterval(move,10,direction)`

This passes our direction variable to a new function called **move** that will be executed every 10 milliseconds by a moveTimer that is created with the setInterval function. 

We also need to add `clearInterval(moveTimer)` to our **keyup** function for both the ArrowLeft and ArrowRight codes.

The **move** function looks like this:

```javascript
function move(direction){
    direction<0 ? (characterLeft-=2) : (characterLeft+=2)

    /* Defining our movement boundaries */
    if (characterLeft>=800){characterLeft=800} 
    if (characterLeft<0){characterLeft=0} 

    character.style.left=characterLeft+'px';
}
```

## Element Animation in CSS

This is a different type of animation than the one we just did in JavaScript.  This involves an animation that, like the sprite sheet animation, is a loop.  Meaning the start and end frames are the same.  In this case we are talking about jumping.

We need to add a function to handle our jump and reference it to pressing the spacebar.

```javascript
/* Functions */
function keydown(e){
    pressed===true
    if(e.code==="ArrowRight")run(1)
    if(e.code==="ArrowLeft")run(-1)
    if(e.code==="Space")jump()
}

function jump(){
    if(character.classList == "animate-jump"){return}
    character.classList.add("background-run")
    character.classList.add("animate-jump")

    setTimeout(removeJump,300) //300ms = length of animation
}

function removeJump(){
    character.classList.remove("animate-jump")
}
```

Finally we need to add the keyframes and timing for our jump to our style.css file.

```css
@keyframes jump{
    0%{top: 150px;}
    30%{top: 100px;}
    70%{top: 100px;}
    100%{top: 150px;}
}

.animate-jump{
    animation: jump 300ms linear;
}
```
 You can find the deployed code here:
[https://setlock10.github.io/jump-guy/](https://setlock10.github.io/jump-guy/)

You can find the github repository here: 
[https://github.com/setlock10/jump-guy](https://github.com/setlock10/jump-guy)

(Almost) any feedback would be appreciated.  If there are points of confusion that you think need to be cleared up please let me know in the comments.

Here are some blog posts that inspired this one:
[https://blog.logrocket.com/build-a-game-with-html-css-javascript/](https://blog.logrocket.com/build-a-game-with-html-css-javascript/)

[https://blog.logrocket.com/making-css-animations-using-a-sprite-sheet/](https://blog.logrocket.com/making-css-animations-using-a-sprite-sheet/)