<main class="Container">
<img class="TitleImage PixelArtImage" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-WalkingSprites.png" alt="Walking Demo Sprites" />
<div class="SpritesheetSlider">
<!-- This div is our character -->
<div class="Character Character--walk-down">
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-Shadow.png" class="Character_shadow PixelArtImage" />
<!-- Spritesheets injected here: -->
</div>
<div class="Navigation flex-center">
<!-- Navigation bubbles injected here: -->
</div>
<button class="NextSpritesheetButton NextSpritesheetButton--prev" onclick="setPreviousActive()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 4 7" shape-rendering="crispEdges">
<metadata>Made with Pixels to Svg https://codepen.io/shshaw/pen/XbxvNj</metadata>
<path stroke="#434343" d="M3 0h1M2 1h1M1 2h1M0 3h1M1 4h1M2 5h1M3 6h1" />
</svg>
</button>
<button class="NextSpritesheetButton NextSpritesheetButton--next" onclick="setNextActive()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 4 7" shape-rendering="crispEdges">
<metadata>Made with Pixels to Svg https://codepen.io/shshaw/pen/XbxvNj</metadata>
<path stroke="#434343" d="M0 0h1M1 1h1M2 2h1M3 3h1M2 4h1M1 5h1M0 6h1" />
</svg>
</button>
</div>
<div class="DemoDirectionUI flex-center">
<button class="DirectionArrow DirectionArrow-left" onclick="setDirection('LEFT')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
<path class="Arrow_outline-top" stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
<path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h5M7 3h5M1 4h4M7 4h5M1 5h3M7 5h5M1 6h4M7 6h5M1 7h5M7 7h5M1 8h11" />
<path class="Arrow_arrow-inset" stroke="#434343" d="M6 3h1M5 4h1M4 5h1" />
<path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 4h1M5 5h2M5 6h2M6 7h1" />
<path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
<path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
<path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
</svg>
</button>
<button class="DirectionArrow DirectionArrow-up" onclick="setDirection('UP')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
<path class="Arrow_outline-top" stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
<path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h11M1 4h5M7 4h5M1 5h4M8 5h4M1 6h3M9 6h3M1 7h11M1 8h11" />
<path class="Arrow_arrow-inset" stroke="#434343" d="M6 4h1M5 5h1M7 5h1" />
<path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 5h1M4 6h5" />
<path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
<path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
<path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
</svg>
</button>
<button class="DirectionArrow DirectionArrow-down DirectionArrow--active" onclick="setDirection('DOWN')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
<path class="Arrow_outline-top" stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
<path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h11M1 4h3M9 4h3M1 5h4M8 5h4M1 6h5M7 6h5M1 7h11M1 8h11" />
<path class="Arrow_arrow-inset" stroke="#434343" d="M4 4h5" />
<path class="Arrow_arrow-body" stroke="#5f5f5f" d="M5 5h3M6 6h1" />
<path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
<path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
<path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
</svg>
</button>
<button class="DirectionArrow DirectionArrow-right" onclick="setDirection('RIGHT')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
<path class="Arrow_outline-top" stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
<path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h5M7 3h5M1 4h5M8 4h4M1 5h5M9 5h3M1 6h5M8 6h4M1 7h5M7 7h5M1 8h11" />
<path class="Arrow_arrow-inset" stroke="#434343" d="M6 3h1M7 4h1M8 5h1" />
<path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 4h1M6 5h2M6 6h2M6 7h1" />
<path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
<path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
<path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
</svg>
</button>
</div>
</main>
<footer>
<p>Characters from <a href="https://store.steampowered.com/app/1064690/Danger_Crew?icid=codepenwalkingdemo" target="_blank">Danger Crew</a>, an RPG about being a programmer!
</footer>
/*
The comments here attempt to explain the implementations of spritesheet animations in Danger Crew.
We'll utilize a dynamic "pixel size" which will allow us to upscale the pixel art in even multiplications.
EX: if an asset is designed as a 10px by 10px square, we can multiply by pixel size of 4 to enlarge the asset to 40px by 40px. This value can be adjusted per screen real estate.
*/
:root {
--pixel-size: 4; /* Try playing with this value! */
}
/*
Allow pixel art images and canvas tags to scale up without losing quality
*/
.PixelArtImage {
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;
}
/*
This is the square crop of the character, each frame designed at natural size 32x32.
Multiplied by dynamic pixel size.
*/
.Character {
width: calc(var(--pixel-size) * 32px);
height: calc(var(--pixel-size) * 32px);
overflow: hidden;
position: relative;
margin: calc(var(--pixel-size) * 6px) auto;
}
.Character_shadow {
width: calc(var(--pixel-size) * 32px);
height: calc(var(--pixel-size) * 32px);
opacity: 0.25;
position: absolute;
}
/* Set up a CSS Animation to move our spritesheet to the LEFT. */
@keyframes walkAnimation {
from {
transform: translate3d(0%,0%,0);
}
to {
transform: translate3d(-100%,0%,0);
}
}
/*
One spritesheet containing layout of 4 by 4 frames, each designed at 32x32.
Total width = 128px multiplied by dynamic pixel size.
The visible part is cropped out via .Character's overflow:hidden rule. This image lives *inside* the .Character container.
*/
.Character_sprite-sheet {
width: calc(var(--pixel-size) * 128px);
animation: walkAnimation 0.6s steps(4) infinite;
/* ^ "Complete the animation in 4 distinct steps". We use 4 because each row of the spritesheet has 4 frames. */
position: absolute;
top:0;
left:0;
display: none; /* For the sake of this demo, JS will control which character spritesheet is visible */
}
.Character_sprite-sheet.active {
display: block;
}
/*
Set up classes that change the y position of underlying spritesheet.
Spritesheet is designed with this layout:
D1 D2 D3 D4
R1 R2 R3 R4 (32px away from top)
U1 U2 U3 U4 (64px away from top)
L1 L2 L3 L4 (96px away from top)
EX: https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-HANK-2-SHEET.png
*/
.Character--walk-down .Character_sprite-sheet {
top: 0;
}
.Character--walk-right .Character_sprite-sheet {
top: calc(var(--pixel-size) * -32px);
}
.Character--walk-up .Character_sprite-sheet {
top: calc(var(--pixel-size) * -64px);
}
.Character--walk-left .Character_sprite-sheet {
top: calc(var(--pixel-size) * -96px);
}
/* Demo UI Stuff ------------------------------------------------- */
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
background-image: linear-gradient(180deg, #5DCBE4 0%, #47BCE4 100%);
height: 100%;
min-height: 100vh;
padding-top: calc(var(--pixel-size) * 18px);
font-family: 'Dosis', sans-serif;
}
.Container {
max-width: calc(var(--pixel-size) * 100px);
margin: 0 auto;
}
.TitleImage {
width: calc(var(--pixel-size) * 95px);
display: block;
margin: 0 auto;
}
.NavigationBubble {
-webkit-appearance: none;
width: calc(var(--pixel-size) * 3px);
height: calc(var(--pixel-size) * 3px);
background: #2497cc;
border: 0;
outline: 0;
cursor: pointer;
padding:0;
margin-left: 0.5em;
margin-right: 0.5em;
box-sizing:content-box;
}
.NavigationBubble:hover:not(.active) {
opacity: 0.9;
}
.NavigationBubble.active {
background: #fff;
}
.NextSpritesheetButton {
-webkit-appearance: none;
border:0;
background:none;
padding: 1em;
position: absolute;
top: 50%;
}
.NextSpritesheetButton--prev {
left: 0;
}
.NextSpritesheetButton--next {
right: 0;
}
.NextSpritesheetButton:hover {
cursor: pointer;
background: rgba(255,255,255,0.2);
}
.NextSpritesheetButton:focus {
outline:none;
}
.NextSpritesheetButton svg {
display: block;
width: calc(var(--pixel-size) * 4px);
}
.DirectionArrow {
background: none;
outline: 0;
border: 0;
cursor: pointer;
}
.DirectionArrow svg {
display: block;
width: calc(var(--pixel-size) * 13px);
}
.DirectionArrow--active .Arrow_outline-top { stroke: #5268ec; }
.DirectionArrow--active .Arrow_surface { stroke: #639bff; }
.DirectionArrow--active .Arrow_outline-bottom { stroke: #164496; }
.DirectionArrow--active .Arrow_edge { stroke: #7baaff; }
.DirectionArrow--active .Arrow_front { stroke: #4c8bfd }
.DirectionArrow--active .Arrow_arrow-inset { stroke: #fff; }
.DirectionArrow--active .Arrow_arrow-body { stroke: #fff; }
.SpritesheetSlider {
position: relative;
margin-bottom: calc(var(--pixel-size) * 10px);
}
.flex-center {
display:flex;
justify-content: center;
}
footer {
margin-top: calc(var(--pixel-size) * 20px);
}
@media (min-height: 515px) {
footer {
position: absolute;
left:0;
right:0;
bottom:0;
}
}
footer p {
color: #fff;
text-align: center;
padding: 0.8em;
font-size: 18px;
margin: 0;
border-top: 1px solid rgba(255,255,255,0.4)
}
footer a {
font-weight:bold;
font-size: 18px;
color: #164496;
}
//This stuff just makes the demo's UI work. ---------------
var characterElement = document.querySelector(".Character");
var spritesheets = [
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-HANK-2-SHEET.png",
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-EMMY-SHEET.png",
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-SHIRMOND-SHEET.png",
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-SARA-SHEET.png",
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-PATTY-SHEET.png",
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-JESSIE-SHEET.png",
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-KIM-SHEET.png",
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-MINDY-SHEET.png",
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-ZAK-SHEET.png",
"https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/WalkingDemo-BEAR-SHEET.png",
];
let activeIndex = 0;
let spritesheetElements = "";
let navigationElements = "";
spritesheets.forEach((spritesheet, index) => {
spritesheetElements += `<img src="${spritesheet}" class="PixelArtImage Character_sprite-sheet index-${index}" />`
navigationElements += `<button class="NavigationBubble index-${index}" onclick='setActive(${index})' />`
});
characterElement.insertAdjacentHTML( 'beforeend', spritesheetElements );
document.querySelector(".Navigation").insertAdjacentHTML( 'beforeend', navigationElements );
function setActive(index) {
activeIndex = index;
document.querySelectorAll(`.active`).forEach(node => {
node.classList.remove("active")
})
document.querySelectorAll(`.index-${index}`).forEach(node => {
node.classList.add("active")
})
}
function setDirection(direction) {
[
"Character--walk-down",
"Character--walk-right",
"Character--walk-up",
"Character--walk-left"
].forEach(className => {
characterElement.classList.remove(className)
})
document.querySelector(".DirectionArrow--active").classList.remove("DirectionArrow--active")
var directionClass = "Character--walk-down";
if (direction === "DOWN") {
document.querySelector(".DirectionArrow-down").classList.add("DirectionArrow--active")
}
if (direction === "LEFT") {
directionClass = "Character--walk-left"
document.querySelector(".DirectionArrow-left").classList.add("DirectionArrow--active")
}
if (direction === "RIGHT") {
directionClass = "Character--walk-right"
document.querySelector(".DirectionArrow-right").classList.add("DirectionArrow--active")
}
if (direction === "UP") {
directionClass = "Character--walk-up"
document.querySelector(".DirectionArrow-up").classList.add("DirectionArrow--active")
}
characterElement.classList.add(directionClass)
}
function setPreviousActive() {
activeIndex = activeIndex > 0 ? activeIndex - 1 : spritesheets.length - 1;
setActive(activeIndex)
}
function setNextActive() {
activeIndex = activeIndex < spritesheets.length - 1 ? activeIndex + 1 : 0;
setActive(activeIndex)
}
//Kick it off!
setActive(activeIndex);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.