<article class="demo">
<section>
<div id="lea-running" class="animated-sprite image-rendering-pixelated" data-frames="6" data-fps="18">
<img src="https://auroratide.com/posts/pixelart-and-the-image-rendering-paradox/lea-running.png" alt="Lea is running from left to right." />
</div>
</section>
<section class="radio-buttons">
<input type="radio" id="image-rendering-auto-selection" name="image-rendering-selection" value="auto" />
<label for="image-rendering-auto-selection">Auto</label>
<input type="radio" id="image-rendering-pixelated-selection" name="image-rendering-selection" value="pixelated" checked />
<label for="image-rendering-pixelated-selection">Pixelated</label>
<input type="radio" id="image-rendering-crisp-edges-selection" name="image-rendering-selection" value="crisp-edges" />
<label for="image-rendering-crisp-edges-selection">Crisp Edges</label>
</section>
<footer>
<p>Pixelart is of Lea from the game <a href="http://cross-code.com/">Cross Code</a> by <a href="http://www.radicalfishgames.com/">Radical Fish Games</p>
</footer>
</article>
.image-rendering-auto {
image-rendering: auto;
}
.image-rendering-pixelated {
image-rendering: crisp-edges; /* firefox */
image-rendering: pixelated;
}
.image-rendering-crisp-edges {
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
}
/* Layout
* ====================================================*/
.demo {
font-family: "Lato", "Lucida Grande", "Lucida Sans Unicode", Tahoma, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.demo section {
padding: 0.5rem 0;
}
/* Sprites
* ====================================================*/
#lea-running {
width: 128px;
height: 128px;
}
.animated-sprite {
overflow: hidden;
}
.animated-sprite img {
position: relative;
}
/* Options
* ====================================================*/
.radio-buttons {
position: relative;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.radio-buttons input[type="radio"] {
position: absolute;
opacity: 0;
}
.radio-buttons label {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 1rem;
line-height: 1.2;
padding: 0.625em 1em;
margin: 0 0.25em 0.5em;
min-width: 6em;
background: #444857;
color: #ffffff;
white-space: nowrap;
cursor: pointer;
box-shadow: 0.125em 0.125em rgba(255, 255, 255, 0.5) inset, -0.125em -0.125em rgba(0, 0, 0, 0.5) inset, 0.125em 0 #000, -0.125em 0 #000, 0 0.125em #000, 0 -0.125em #000;
}
.radio-buttons label:hover {
background: #5a5f73;
}
.radio-buttons input[type="radio"]:checked + label,
.radio-buttons label:active {
box-shadow: -0.125em -0.125em rgba(255, 255, 255, 0.5) inset, 0.125em 0.125em rgba(0, 0, 0, 0.5) inset, 0.125em 0 #000, -0.125em 0 #000, 0 0.125em #000, 0 -0.125em #000;
}
.radio-buttons input[type="radio"]:checked + label {
background: #1e1f26;
cursor: auto;
}
function animateSprite(elem) {
const fps = parseInt(elem.dataset.fps) || 60
const frames = parseInt(elem.dataset.frames)
const img = elem.querySelector('img')
img.style.width = `${frames * 100}%`
img.style.left = '0%'
let lastTickTime = 0
const interval = 1000 / fps
function tick(time) {
if (time - lastTickTime >= interval) {
const currentFrame = parseInt(img.style.left) / -100
img.style.left = `${-100 * ((currentFrame + 1) % frames)}%`
lastTickTime = time
}
requestAnimationFrame(tick)
}
requestAnimationFrame(tick)
}
const lea = document.getElementById('lea-running')
const options = {}
document.querySelectorAll('input[name="image-rendering-selection"]').forEach(elem => {
options[elem.value] = elem
})
animateSprite(lea)
Object.entries(options).forEach(([option, elem]) => {
elem.oninput = () => {
Object.keys(options).forEach((key) => lea.classList.remove(`image-rendering-${key}`))
lea.classList.add(`image-rendering-${elem.value}`)
}
})
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.