Instantly share code, notes, and snippets.
Last active
February 27, 2019 19:23
-
Star
(1)
1
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save mtandre/742eb74dd3750e878190 to your computer and use it in GitHub Desktop.
"Amazon inspired" hover navigation menu with trajectory projection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
var inTriangle = false, link = false, | |
navigation = document.querySelector('.navigation--sections'), | |
allNavItems = document.querySelectorAll('.navigation--item'), | |
x0, y0, x1, x2, x3, y1, y2, y3, hoverDelay; | |
navigation.addEventListener('mouseenter', onmouseenter); | |
navigation.addEventListener('mouseleave', onmouseleave); | |
function isInsideTriangle() { | |
// barycentric coordinates | |
var b0 = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1), | |
b1 = ((x2 - x0) * (y3 - y0) - (x3 - x0) * (y2 - y0)) / b0, | |
b2 = ((x3 - x0) * (y1 - y0) - (x1 - x0) * (y3 - y0)) / b0, | |
b3 = ((x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0)) / b0; | |
return b1 > 0 && b2 > 0 && b3 > 0; | |
} | |
function getParent(elem, classname) { | |
for ( ; elem && elem !== document; elem = elem.parentNode ) { | |
if ( elem.classList.contains( classname ) ) { | |
return elem; | |
} | |
} | |
return false; | |
} | |
function isHover(e) { | |
return (e.parentElement.querySelector(':hover') === e); | |
} | |
function onmouseenter(event) { | |
document.addEventListener('mousemove', onmousemove); | |
} | |
function onmouseleave(event) { | |
document.removeEventListener('mousemove', onmousemove); | |
} | |
function onmousemove(event) { | |
// set current mouse coordinates | |
x0 = event.clientX; | |
y0 = event.clientY; | |
// get menu item | |
var newLink = getParent(event.target, 'navigation--item'); | |
if (!newLink) { | |
link = null; | |
return; | |
} | |
// new link, create baseline triangle | |
if (!link) { | |
clearTimeout(hoverDelay); | |
hoverDelay = setTimeout( function() { | |
console.log('new link, create baseline triangle'); | |
link = newLink; | |
// safety - confirm there is only one active item | |
for (i = 0; i < allNavItems.length; ++i) { | |
allNavItems[i].classList.remove('dropdown--visible'); | |
} | |
// active dropdown | |
link.classList.add('dropdown--visible'); | |
// get the next mouseover el: dropdown's dims and coords | |
var next = link.lastElementChild.getBoundingClientRect(); | |
// set triangle’s top point | |
x1 = x0; | |
y1 = y0; | |
// set triangle’s right point | |
x2 = next.right; // next.left + (0.33 * next.width); | |
y2 = next.top; | |
// set triangle’s left point | |
x3 = next.left; | |
y3 = next.top; | |
}, 100); // the delay user must be hovering on first interaction | |
} else if (link !== newLink) { | |
// different link | |
console.log('different link'); | |
if (isInsideTriangle()) { | |
// on the way to the same link, build narrower triangle, set timer | |
console.log('on the way to the same link, build narrower triangle'); | |
// get the next mouseover el: dropdown's dims and coords | |
var next = link.lastElementChild.getBoundingClientRect(); | |
// set triangle’s top point | |
x1 = x0; | |
y1 = y0; | |
// set triangle’s right point | |
x2 = next.right; // next.left + (0.33 * next.width); | |
y2 = next.top; | |
// set triangle’s left point | |
x3 = next.left; | |
y3 = next.top; | |
} else { | |
// different link, reset and create new triangle | |
console.log('different link, reset and create new triangle'); | |
for (i = 0; i < allNavItems.length; ++i) { | |
allNavItems[i].classList.remove('dropdown--visible'); | |
} | |
link = newLink; | |
// active dropdown | |
link.classList.add('dropdown--visible'); | |
// get the next mouseover el: dropdown's dims and coords | |
var next = link.lastElementChild.getBoundingClientRect(); | |
// set triangle’s top point | |
x1 = x0; | |
y1 = y0; | |
// set triangle’s right point | |
x2 = next.right; // next.left + (0.33 * next.width); | |
y2 = next.top; | |
// set triangle’s left point | |
x3 = next.left; | |
y3 = next.top; | |
} | |
clearTimeout(hoverDelay); | |
} else { | |
// made it to the same link, clear timer | |
console.log('made it to the same link'); | |
clearTimeout(hoverDelay); | |
} | |
return; | |
} | |
function onmouseleave(event) { | |
// clear timers | |
console.log('mouseleave'); | |
clearTimeout(hoverDelay); | |
// remove any hover classes | |
for (i = 0; i < allNavItems.length; ++i) { | |
allNavItems[i].classList.remove('dropdown--visible'); | |
} | |
} | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment