<div id="app">
<nav>
<ul>
<li v-for="(item, index) in menuItems"
:key="`item-${index}`"
class="menu-item"
:class="{
'selected':item.selected,
'dimmed':item.dimmed
}"
@mouseover="selectItem(index)"
@mouseleave="selectItem(-1)">
<span class="char"
v-for="(char, charIndex) in item.chars"
:key="`char-${charIndex}`"
:ref="`char-${index}-${charIndex}`">{{char}}</span>
<span class="arrow">➔</span>
</li>
</ul>
</nav>
<!-- <p>
selectedItem: {{ selectedItem }}
</p> -->
</div>
body {
background-color: #F1F0E9;
}
.menu-item {
font-size: 5em;
list-style: none;
text-transform: uppercase;
font-family: sans-serif;
text-align: center;
cursor: pointer;
transition: margin-left 0.5s ease-out, opacity 0.5s ease-out;
}
.selected {
margin-left: -90px;
}
.menu-item > .arrow {
opacity: 0;
transition: opacity 0.7s ease-out;
}
.selected > .arrow {
opacity: 1;
}
.dimmed {
opacity: 0.3;
}
.char {
display: inline-block;
min-width: 0.3em;
}
// reference https://dribbble.com/shots/6109210-Chez-Studio-Menu-Hover-Animation
const itemsList = [
'About',
'Works',
'News/Blog',
'spark'
]
const LEFT = -1
const RIGHT = 1
new Vue({
el: '#app',
data: {
selectedItem: -1
},
methods: {
selectItem(id) {
this.selectedItem = id;
this.menuItems.forEach((item, index) => {
const direction = item.selected ? LEFT : RIGHT;
this.animateChars(index, item.label.length, direction);
})
},
animateChars (id, charLength, direction) {
for(let c=0;c < charLength; c++){
const refId = `char-${id}-${c}`;
const char = this.$refs[refId];
TweenMax.killTweensOf(char);
TweenMax.to(char, 0.5, {scaleX: direction});
}
}
},
computed: {
menuItems () {
return itemsList.map((item, index) => {
const isSelected = this.selectedItem === index;
const otherButtonIsSelected = this.selectedItem !== -1
return {
label: item,
selected: isSelected,
dimmed: !isSelected && otherButtonIsSelected,
chars: item.split('')
}
})
}
}
})
This Pen doesn't use any external CSS resources.