Created
April 11, 2024 16:10
-
-
Save tasinttttttt/b95305197f3fc538238b2c6d15fa9d98 to your computer and use it in GitHub Desktop.
Glass pill menu effect
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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Document</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<style> | |
body { | |
font-family: AXIS Font Latin W04, ヒラギノ角ゴ Pro W3, | |
Hiragino Kaku Gothic Pro, inter, system, -apple-system, system-ui, | |
Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, | |
BlinkMacSystemFont, Oxygen, Open Sans, Helvetica Neue, Helvetica, | |
DM Sans, Arial, Lucida Grande, Kohinoor Devanagari; | |
} | |
/* .magnifier:before { | |
content: ""; | |
position: absolute; | |
top: 0; | |
left: 50%; | |
width: 100%; | |
height: 100%; | |
background: rgba(255, 255, 255, 0.7); | |
transform: skewX(-30deg); | |
transition: 0.5s; | |
z-index: 60; | |
backdrop-filter: blur(5px); | |
} */ | |
.magnifier { | |
touch-action: manipulation; | |
position: relative; | |
/* border: 1px solid #9997; */ | |
border-radius: 50px; | |
background-image: radial-gradient( | |
75% 50% at 50% 0%, | |
#f4feff66, | |
transparent | |
), | |
radial-gradient(75% 35% at 50% 80%, #4444, transparent); | |
/* box-shadow: inset 0 -2px 4px 1px #4444, inset 0 -4px 4px 1px #4444, */ | |
/* inset 0 0 2px 1px rgba(255, 255, 255, 0.2), | |
0 1px 4px 1px rgba(17, 110, 231, 0.2), | |
0 1px 4px 1px rgba(0, 0, 0, 0.1); */ | |
text-shadow: 0 1px 4px #0003; | |
color: #000d; | |
box-shadow: inset 0 0px 8px #000b; | |
} | |
.magnifierShadow { | |
width: 100%; | |
position: absolute; | |
height: 100%; | |
box-shadow: 0 2px 4px #000; | |
/* z-index: 36; */ | |
width: 22ch; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
margin: auto; | |
border-radius: 9999px; | |
height: 4rem; | |
} | |
.magnifier::after { | |
content: ""; | |
position: absolute; | |
top: 1px; | |
left: 50%; | |
transform: translateX(-50%); | |
border-radius: 50px; | |
/* width: 80%; | |
height: 40%; */ | |
width: 86%; | |
height: 70%; | |
/* background-image: linear-gradient(to bottom, #f4feff, transparent); */ | |
background-image: linear-gradient(to bottom, #fffa, transparent); | |
opacity: 0.75; | |
} | |
</style> | |
</head> | |
<body> | |
<nav | |
class="absolute top-0 left-0 right-0 bottom-0 m-auto w-[440px] h-32 overflow-hidden" | |
> | |
<div | |
class="menu relative w-full rounded-[9999px] h-24 cursor-grab active:cursor-grabbing" | |
> | |
<div class="magnifierShadow"></div> | |
<div | |
class="magnifier absolute top-0 left-0 bottom-0 right-0 w-auto z-20 bg-white overflow-hidden m-auto h-16 rounded-[9999px] select-none" | |
style="width: 22ch" | |
> | |
<div | |
class="magnified-items absolute top-0 bottom-0 m-auto flex flex-row gap-4 items-center justify-between text-4xl font-bold w-full transition-all duration-500 ease-out align-center leading-none" | |
> | |
<div | |
class="magnified-item cursor-pointer uppercase leading-none duration-300 ease-out" | |
> | |
Music | |
</div> | |
<div | |
class="magnified-item cursor-pointer uppercase transition-all leading-none duration-300 ease-out" | |
> | |
Gigs | |
</div> | |
<div | |
class="magnified-item cursor-pointer uppercase transition-all leading-none duration-300 ease-out" | |
> | |
About | |
</div> | |
<div | |
class="magnified-item cursor-pointer uppercase transition-all leading-none duration-300 ease-out" | |
> | |
Links | |
</div> | |
</div> | |
</div> | |
<div | |
class="top-0 bottom-0 left-0 m-auto w-full absolute backdrop-blur-[1.5px] rounded-[9999px] h-full z-10 pointer-events-none" | |
></div> | |
<div | |
class="top-0 bottom-0 left-0 m-auto absolute rounded-[9999px] gradient h-full z-10 pointer-events-none w-3/12 bg-gradient-to-r from-white to-white/0" | |
></div> | |
<div | |
class="top-0 bottom-0 right-0 m-auto absolute rounded-[9999px] gradient h-full z-10 pointer-events-none w-3/12 bg-gradient-to-l from-white to-white/0" | |
></div> | |
<div | |
class="unmagnified-items absolute top-0 bottom-0 m-auto flex flex-row items-center justify-between text-sm w-full transition-all duration-500 ease-out align-center leading-none w-full z-0 opacity-80 select-none cursor-grab active:cursor-grabbing duration-300" | |
style="width: 40ch" | |
> | |
<div | |
class="unmagnified-item cursor-pointer uppercase transition-all leading-none duration-500" | |
> | |
Music | |
</div> | |
<div | |
class="unmagnified-item cursor-pointer uppercase transition-all leading-none duration-500" | |
> | |
Gigs | |
</div> | |
<div | |
class="unmagnified-item cursor-pointer uppercase transition-all leading-none duration-500" | |
> | |
About | |
</div> | |
<div | |
class="unmagnified-item cursor-pointer uppercase transition-all leading-none duration-500" | |
> | |
Links | |
</div> | |
</div> | |
<!-- <div | |
class="w-1 h-full bg-red-400 z-30 absolute left-0 top-0 menuCenterDebug" | |
></div> --> | |
</div> | |
</nav> | |
<div | |
class="debug opacity-0 fixed bottom-3 left-3 bg-yellow-200 py-2 px-4" | |
></div> | |
<script type="module"> | |
import { gestures } from "./swiped-events.min.js"; | |
function updateDebug(data) { | |
const debug = document.querySelector(".debug"); | |
let str = ""; | |
Object.entries(data).map(([key, value]) => { | |
str += `${key}: ${value}<br/>`; | |
}); | |
debug.innerHTML = str; | |
const menu = document.querySelector(".menu"); | |
} | |
(() => { | |
gestures(); | |
const state = { | |
activeIndex: 0, | |
menuWidth: 0, | |
center: 0, | |
magnifiedPosition: 0, | |
unmagnifiedPosition: 0, | |
stripPosition: 0, | |
itemsPosLeft: [], | |
unmagnifiedItemsPosLeft: [], | |
shineAnimationDirection: false, | |
}; | |
const magnifier = document.querySelector(".magnifier"); | |
const magnifiedItemsContainer = | |
document.querySelector(".magnified-items"); | |
const magnifiedItems = document.querySelectorAll(".magnified-item"); | |
const unmagnifiedItemsContainer = | |
document.querySelector(".unmagnified-items"); | |
const unmagnifiedItems = document.querySelectorAll(".unmagnified-item"); | |
const activeMenuItem = document.querySelector(".active-menu-item"); | |
const menu = document.querySelector(".menu"); | |
function setMagnifiedActiveItem() { | |
state.activeIndex = [].indexOf.call(magnifiedItems, this); | |
setMenuCenter(); | |
render(); | |
} | |
function setUnmagnifiedActiveItem() { | |
state.activeIndex = [].indexOf.call(unmagnifiedItems, this); | |
setMenuCenter(); | |
render(); | |
} | |
function setActiveItemIndex(i) { | |
state.activeIndex = i; | |
setMenuCenter(); | |
render(); | |
} | |
function setPositions() { | |
const menuBoundingClient = menu.getBoundingClientRect(); | |
magnifiedItems.forEach((item, i) => { | |
const boundingClientRect = item.getBoundingClientRect(); | |
state.itemsPosLeft[i] = | |
boundingClientRect.left - menuBoundingClient.left; | |
}); | |
unmagnifiedItems.forEach((item, i) => { | |
const boundingClientRect = item.getBoundingClientRect(); | |
state.unmagnifiedItemsPosLeft[i] = | |
boundingClientRect.left - menuBoundingClient.left; | |
}); | |
setMenuCenter(); | |
} | |
function initListeners() { | |
state.total = magnifiedItems.length; | |
magnifiedItems.forEach((item, i) => { | |
item.addEventListener("click", setMagnifiedActiveItem, false); | |
}); | |
unmagnifiedItems.forEach((item, i) => { | |
item.addEventListener("click", setUnmagnifiedActiveItem, false); | |
}); | |
menu.addEventListener("swiperight", function (e) { | |
setActiveItemIndex(Math.max(1, state.activeIndex) - 1); | |
}); | |
menu.addEventListener("swipeleft", function (e) { | |
setActiveItemIndex( | |
Math.min(state.total - 2, state.activeIndex) + 1 | |
); | |
}); | |
window.addEventListener("resize", render); | |
} | |
function setMenuCenter() { | |
state.menuWidth = menu.clientWidth; | |
state.center = menu.clientWidth * 0.5; | |
} | |
function animateShine() { | |
// if (!state.shineAnimationDirection) { | |
// magnifier.classList.add("before:-left-full"); | |
// } else { | |
// magnifier.classList.remove("before:-left-full"); | |
// } | |
state.shineAnimationDirection = !state.shineAnimationDirection; | |
} | |
function render() { | |
renderMagnified(); | |
renderUnmagnified(); | |
animateShine(); | |
updateDebug(state); | |
} | |
function renderMagnified() { | |
let pos = state.itemsPosLeft[state.activeIndex]; | |
state.magnifiedPosition = | |
state.center - | |
pos - | |
magnifiedItems[state.activeIndex].clientWidth * 0.5; | |
magnifiedItems.forEach((item) => | |
item.classList.add("blur-[2px]", "opacity-20", "cursor-pointer") | |
); | |
if (state.activeIndex + 1 < state.total) { | |
magnifiedItems[state.activeIndex + 1].classList.add( | |
"skew-y-6", | |
"rotate-20", | |
"scale-150", | |
"origin-left" | |
); | |
} | |
if (state.activeIndex - 1 > 0) { | |
magnifiedItems[state.activeIndex - 1].classList.add( | |
"-skew-y-6", | |
"-rotate-20", | |
"scale-150", | |
"origin-right" | |
); | |
} | |
magnifiedItems[state.activeIndex].classList.remove( | |
"blur-[2px]", | |
"opacity-20", | |
"cursor-pointer", | |
"-skew-y-6", | |
"rotate-20", | |
"scale-150", | |
"skew-y-6", | |
"-rotate-20", | |
"origin-left", | |
"origin-right" | |
); | |
magnifiedItemsContainer.style.left = state.magnifiedPosition + "px"; | |
} | |
function renderUnmagnified() { | |
let pos = state.unmagnifiedItemsPosLeft[state.activeIndex]; | |
state.unmagnifiedPosition = | |
state.center - | |
pos - | |
unmagnifiedItems[state.activeIndex].clientWidth * 0.5; | |
unmagnifiedItemsContainer.style.left = | |
state.unmagnifiedPosition + "px"; | |
unmagnifiedItems.forEach((item) => | |
item.classList.remove("opacity-0") | |
); | |
unmagnifiedItems[state.activeIndex].classList.add("opacity-0"); | |
} | |
setPositions(); | |
initListeners(); | |
render(); | |
})(); | |
</script> | |
</body> | |
</html> |
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
var eventstart, eventend, eventmove, eventcancel; | |
window.navigator.pointerEnabled | |
? ((eventstart = "pointerdown"), | |
(eventend = "pointerup"), | |
(eventmove = "pointermove"), | |
(eventcancel = "pointercancel")) | |
: window.navigator.msPointerEnabled | |
? ((eventstart = "MSPointerDown"), | |
(eventend = "MSPointerUp"), | |
(eventmove = "MSPointerMove"), | |
(eventcancel = "MSPointerCancel")) | |
: "ontouchstart" in window | |
? ((eventstart = "touchstart"), | |
(eventend = "touchend"), | |
(eventmove = "touchmove"), | |
(eventcancel = "touchcancel")) | |
: ((eventstart = "mousedown"), | |
(eventend = "mouseup"), | |
(eventmove = "mousemove"), | |
(eventcancel = "mouseout")); | |
function trigger(a, b, c, d) { | |
if (!b) | |
return void console.error( | |
"No event was provided. You do need to provide one." | |
); | |
if ( | |
("string" == typeof a && (a = document.querySelector(a)), | |
document.createEvent) | |
) { | |
var e = document.createEvent("Events"); | |
e.initEvent(b, !0, !1), | |
(e.data = c), | |
(e.originalEvent = d), | |
a.dispatchEvent(e); | |
} | |
} | |
var gestures = function () { | |
var g = Math.abs; | |
function a(a) { | |
return "tagName" in a ? a : a.parentNode; | |
} | |
function b(a, b, c, d) { | |
return g(a - b) >= g(c - d) | |
? 0 < a - b | |
? "left" | |
: "right" | |
: 0 < c - d | |
? "up" | |
: "down"; | |
} | |
function c(a) { | |
if (((n = null), k.last)) | |
try { | |
k && k.el && (trigger(k.el, "longtap", null, a), (k = {})); | |
} catch (a) {} | |
} | |
function d() { | |
n && clearTimeout(n), (n = null); | |
} | |
function f() { | |
h && clearTimeout(h), | |
j && clearTimeout(j), | |
i && clearTimeout(i), | |
n && clearTimeout(n), | |
(h = j = i = n = null), | |
(k = {}); | |
} | |
var h, | |
i, | |
j, | |
k = {}, | |
l = 150, | |
m = 20; | |
/android/gim.test(navigator.userAgent) && (l = 200); | |
var n; | |
(function () { | |
var o, | |
p, | |
q = document.body; | |
q.addEventListener(eventstart, function (b) { | |
if ( | |
((o = Date.now()), (p = o - (k.last || o)), "mousedown" === eventstart) | |
) | |
(k.el = a(b.target)), | |
"ripple" === b.target.nodeName && (k.el = b.target.parentNode), | |
h && clearTimeout(h), | |
(k.x1 = b.pageX), | |
(k.y1 = b.pageY); | |
else if (1 === b.touches.length) { | |
if (!!b.target.disabled) return; | |
(k.el = a(b.touches[0].target)), | |
h && clearTimeout(h), | |
(k.x1 = b.touches[0].pageX), | |
(k.y1 = b.touches[0].pageY); | |
} | |
0 < p && 450 >= p && (k.isDoubleTap = !0), | |
(k.last = o), | |
(n = setTimeout(c, 750, b)); | |
}), | |
q.addEventListener(eventmove, function (a) { | |
if ((d(), "mousemove" === eventmove)) | |
(k.x2 = a.pageX), (k.y2 = a.pageY); | |
else if (1 === a.touches.length) | |
(k.x2 = a.touches[0].pageX), | |
(k.y2 = a.touches[0].pageY), | |
(k.move = !0); | |
else if (2 === a.touches.length); | |
}), | |
q.addEventListener(eventend, function (a) { | |
d(); | |
!k.el || | |
((k.x2 && g(k.x1 - k.x2) > m) || (k.y2 && g(k.y1 - k.y2) > m) | |
? (i = setTimeout(function () { | |
if (k && k.el) { | |
var c = b(k.x1, k.x2, k.y1, k.y2); | |
trigger(k.el, "swipe", c, a), | |
trigger(k.el, "swipe" + c, null, a), | |
(k = {}); | |
} | |
}, 0)) | |
: "last" in k && | |
(j = setTimeout(function () { | |
k && k.isDoubleTap | |
? k && | |
k.el && | |
(trigger(k.el, "dbltap", null, a), | |
a.preventDefault(), | |
(k = {})) | |
: (h = setTimeout(function () { | |
(h = null), | |
k && k.el && !k.move | |
? (trigger(k.el, "tap", null, a), (k = {})) | |
: f(); | |
}, l)); | |
}, 0))); | |
}), | |
q.addEventListener("touchcancel", f); | |
})(); | |
}; | |
function disableTextSelection(a, b) { | |
if (a) { | |
if (b && "string" == typeof a) { | |
var c = Array.prototype.slice.call(document.querySelectorAll(a)); | |
c.map(function (a) { | |
a.classList.add("disable-user-select"); | |
}); | |
} else | |
"string" == typeof a && | |
((a = document.querySelector(a)), | |
a.classList.add("disable-user-select")); | |
var d = document.head.querySelector(".disable-user-select"); | |
d || | |
((d = document.createElement("style")), | |
(d.className = "disable-user-select"), | |
(d.innerHTML = | |
".disable-user-select, .disable-user-select * { user-select: none; -webkit-user-select: none; -ms-user-select: none; }"), | |
document.head.appendChild(d)); | |
} | |
} | |
function enableTextSelection(a, b) { | |
if (b && "string" == typeof a) { | |
var c = Array.prototype.slice.call(document.querySelectorAll(a)); | |
c.map(function (a) { | |
a.classList.remove("disable-user-select"); | |
}); | |
} else { | |
if (("string" == typeof a && (a = document.querySelector(a)), !a)) return; | |
a.classList.remove("disable-user-select"); | |
} | |
} | |
export { | |
eventstart, | |
eventend, | |
eventmove, | |
eventcancel, | |
trigger, | |
gestures, | |
disableTextSelection, | |
enableTextSelection, | |
}; | |
//# sourceMappingURL=gestures.mjs.map |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment