Codevember day 4: sapphire! Uses the Web Animations API, so if it doesn't work in your browser, that's why.
Click anywhere to see the effect.
A Pen by Adrian C Miranda on CodePen.
Codevember day 4: sapphire! Uses the Web Animations API, so if it doesn't work in your browser, that's why.
Click anywhere to see the effect.
A Pen by Adrian C Miranda on CodePen.
<div id="gemstones" data-state="sapphire"></div> |
const gemstones = { | |
garnet: 'january', | |
amethyst: 'february', | |
aquamarine: 'march', | |
diamond: 'april', | |
emerald: 'may', | |
alexandrite: 'june', | |
ruby: 'july', | |
peridot: 'august', | |
sapphire: 'september', | |
tourmaline: 'october', | |
topaz: 'november', | |
turquoise: 'december' | |
}; | |
function mapLetters(word, prefix) { | |
const seen = {}; | |
return [...word].map(char => { | |
if (seen[char] !== undefined) { | |
++seen[char]; | |
return `<span data-flip-key="${prefix}-${char}-${seen[char]}">${char}</span>`; | |
} else { | |
seen[char] = 0; | |
return `<span data-flip-key="${prefix}-${char}">${char}</span>`; | |
} | |
}); | |
} | |
const gemstonesHTML = Object.keys(gemstones).map(gemstone => { | |
const month = gemstones[gemstone]; | |
return ` | |
<div class="gemstone" id="${gemstone}"> | |
<div class="month"> | |
${mapLetters(month, 'month').join('')} | |
</div> | |
<div class="gem"> | |
${mapLetters(gemstone, 'gem').join('')} | |
</div> | |
</div> | |
` | |
}).join('\n'); | |
const gemstonesElm = document.getElementById('gemstones'); | |
gemstonesElm.innerHTML = gemstonesHTML; | |
// state machine | |
function cycle(states, action) { | |
const statesMap = {}; | |
states.forEach((state, i) => { | |
statesMap[state] = { | |
[action]: states[i + 1] || states[0] | |
}; | |
}); | |
return statesMap; | |
} | |
const machine = cycle(Object.keys(gemstones), 'CLICK'); | |
function transition(state, action) { | |
return machine[state][action]; | |
} | |
// FLIPPING! | |
const flipping = new Flipping({ | |
parentElement: gemstonesElm, | |
duration: 600 | |
}); | |
function update(state) { | |
gemstonesElm.setAttribute('data-state', state); | |
} | |
let currentState = 'sapphire'; | |
document.body.addEventListener('click', flipping.wrap(() => { | |
currentState = transition(currentState, 'CLICK'); | |
update(currentState); | |
})); | |
update(currentState); |
<script src="https://unpkg.com/[email protected]/dist/flipping.web.js"></script> |
@import url('https://fonts.googleapis.com/css?family=PT+Serif'); | |
$gemstones: | |
'garnet', | |
'amethyst', | |
'aquamarine', | |
'diamond', | |
'emerald', | |
'alexandrite', | |
'ruby', | |
'peridot', | |
'sapphire', | |
'tourmaline', | |
'topaz', | |
'turquoise'; | |
@each $gemstone in $gemstones { | |
[data-state="#{$gemstone}"] .gemstone:not(##{$gemstone}) { | |
display: none; | |
} | |
} | |
span { | |
display: inline-block; | |
// will-change: transform; | |
} | |
.month { | |
font-size: 3vmin; | |
letter-spacing: 1vmin; | |
text-transform: uppercase; | |
opacity: 0.5; | |
} | |
.gem { | |
font-size: 10vmin; | |
font-family: PT Serif, serif; | |
} | |
html, body { | |
width: 100%; | |
height: 100%; | |
margin: 0; | |
padding: 0; | |
background-color: #191919; | |
color: white; | |
user-select: none; | |
} | |
body { | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
align-items: center; | |
text-align: center; | |
} | |
*, *:before, *:after { | |
box-sizing: border-box; | |
position: relative; | |
} |