Skip to content

Instantly share code, notes, and snippets.

@tasinttttttt
Created April 11, 2024 16:10
Show Gist options
  • Save tasinttttttt/b95305197f3fc538238b2c6d15fa9d98 to your computer and use it in GitHub Desktop.
Save tasinttttttt/b95305197f3fc538238b2c6d15fa9d98 to your computer and use it in GitHub Desktop.
Glass pill menu effect
<!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>
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