<!-- @NOTE: the onclicks are used for touch devices since I'm lazy and didnt use <a> tags -->
<div class="Menu">
<ul class="Menu-list" data-offset="10">
<li class="Menu-list-item" data-offset="20" onclick>
Home
<span class="Mask"><span>Home</span></span>
<span class="Mask"><span>Home</span></span>
</li>
<li class="Menu-list-item" data-offset="16" onclick>
About
<span class="Mask"><span>About</span></span>
<span class="Mask"><span>About</span></span>
</li>
<li class="Menu-list-item" data-offset="12" onclick>
Work
<span class="Mask"><span>Work</span></span>
<span class="Mask"><span>Work</span></span>
</li>
<li class="Menu-list-item" data-offset="8" onclick>
Contact
<span class="Mask"><span>Contact</span></span>
<span class="Mask"><span>Contact</span></span>
</li>
</ul>
</div>
// === Variables =====
$perspective: 60rem;
$font-size: 4.25rem;
$split-position: 49%;
$split-thickness: 4px;
$split-color: #FF2C75;
// === Settings =====
%font-settings {
font-family: "Gilroy ExtraBold", system-ui, sans-serif;
font-style: normal;
font-weight: normal;
-webkit-font-smoothing: antialiased;
-webkit-font-kerning: normal;
-webkit-text-size-adjust: 100%;
}
// === Codepen Setup =====
html,
body {
width: 100vw;
height: 100vh;
}
body {
@extend %font-settings;
background: linear-gradient(45deg, #02001F,#1F1B4E);
transform-style: preserve-3d;
transform: perspective($perspective);
position: fixed;
display: flex;
align-items: center;
justify-content: center;
}
// === Menu <ul> =====
.Menu-list {
font-size: $font-size;
line-height: 1.2;
text-transform: uppercase;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
transform: rotateX(-10deg) rotateY(20deg); // overwritten by JS
}
// === Menu item =====
.Menu-list-item {
position: relative;
color: transparent;
cursor: pointer;
// === Split Line =====
&::before {
content: '';
display: block;
position: absolute;
top: $split-position;
left: -10%;
right: -10%;
height: $split-thickness;
border-radius: $split-thickness;
margin-top: -($split-thickness / 2);
background: $split-color;
transform: scale(0);
transition: transform .8s cubic-bezier(.16,1.08,.38,.98);
z-index: 1;
}
}
// === Top/bottom mask =====
.Mask {
display: block;
position: absolute;
overflow: hidden;
color: $split-color;
top: 0;
height: $split-position;
transition: all .8s cubic-bezier(.16,1.08,.38,.98);
span { display: block; }
}
// === Bottom mask specific =====
.Mask + .Mask {
top: $split-position - 0.1;
height: 100 - $split-position + 0.1;
span { transform: translateY(-$split-position); }
}
// === Where the fun stuff happens =====
.Menu-list-item:hover,
.Menu-list-item:active {
.Mask { color: #FFF; transform: skewX(12deg) translateX(5px); }
.Mask + .Mask { transform: skewX(12deg) translateX(-5px); }
&::before { transform: scale(1); }
}
View Compiled
var $menu = $('.Menu-list'),
$item = $('.Menu-list-item'),
w = $(window).width(), //window width
h = $(window).height(); //window height
$(window).on('mousemove', function(e) {
var offsetX = 0.5 - e.pageX / w, //cursor position X
offsetY = 0.5 - e.pageY / h, //cursor position Y
dy = e.pageY - h / 2, //@h/2 = center of poster
dx = e.pageX - w / 2, //@w/2 = center of poster
theta = Math.atan2(dy, dx), //angle between cursor and center of poster in RAD
angle = theta * 180 / Math.PI - 90, //convert rad in degrees
offsetPoster = $menu.data('offset'),
transformPoster = 'translate3d(0, ' + -offsetX * offsetPoster + 'px, 0) rotateX(' + (-offsetY * offsetPoster) + 'deg) rotateY(' + (offsetX * (offsetPoster * 2)) + 'deg)'; //poster transform
//get angle between 0-360
if (angle < 0) {
angle = angle + 360;
}
//poster transform
$menu.css('transform', transformPoster);
//parallax for each layer
$item.each(function() {
var $this = $(this),
offsetLayer = $this.data('offset') || 0,
transformLayer = 'translate3d(' + offsetX * offsetLayer + 'px, ' + offsetY * offsetLayer + 'px, 20px)';
$this.css('transform', transformLayer);
});
});