<div class="min-h-screen p-16 bg-gray-100">
<div x-data="wheel(wheelItems)"
@keydown.window.tab="usedKeyboard = true"
x-init="init()"
x-title="Wheel">
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"
class="w-full mx-auto max-w-xl fill-current text-2xl duration-700 transition transform"
:style="`--transform-rotate: ${currentRotation}deg;`">
<title>Navigation</title>
<desc id="desc">Large circle of text with clickable navigation buttons</desc>
<path d="
M 100, 100
m -75, 0
a 75,75 0 1,0 150,0
a 75,75 0 1,0 -150,0" id="path-circle" fill="none" />
<text>
<textPath href="#path-circle">
<tspan
tabindex="0"
@click="current = '0'"
@keydown.enter.stop="current = '0'"
class="cursor-pointer"
:class="{
'focus:outline-none': !usedKeyboard,
'text-indigo-500': current == '0'
}">Home</tspan>
<tspan class="">•</tspan>
<tspan
tabindex="0"
@click="current = '1'"
@keydown.enter.stop="current = '1'"
class="cursor-pointer"
:class="{
'focus:outline-none': !usedKeyboard,
'text-indigo-500': current == '1'
}">About</tspan>
<tspan class="">•</tspan>
<tspan
tabindex="0"
@click="current = '2'"
@keydown.enter.stop="current = '2'"
class="cursor-pointer"
:class="{
'focus:outline-none': !usedKeyboard,
'text-indigo-500': current == '2'
}">Contact</tspan>
<tspan class="">•</tspan>
<tspan
tabindex="0"
@click="current = '3'"
@keydown.enter.stop="current = '3'"
class="cursor-pointer"
:class="{
'focus:outline-none': !usedKeyboard,
'text-indigo-500': current == '3'
}">Portfolio</tspan>
<tspan class="">•</tspan>
<tspan
tabindex="0"
@click="current = '4'"
@keydown.enter.stop="current = '4'"
class="cursor-pointer"
:class="{
'focus:outline-none': !usedKeyboard,
'text-indigo-500': current == '4'
}">Another</tspan>
<tspan class="">•</tspan>
</textPath>
</text>
</svg>
<div class="flex flex-col relative z-50 -mt-16 items-center justify-center w-full mx-auto max-w-sm text-center text-xl leading-relaxed">
<svg class="w-16 text-indigo-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" />
</svg>
<h1 class="font-bold text-2xl" x-text="items[current].title"></h1>
<div x-html="items[current].content"></div>
</div>
</div>
</div>
function wheel(items) {
return {
current: 0,
usedKeyboard: false,
rotations: [-67, -5, 65, 142, 220],
possibleRotations: [],
currentRotation: -67,
init() {
this.$watch('current', value => {
this.updateRotation()
})
},
updateRotation() {
const previousRotation = this.currentRotation
const multiple = Math.floor(this.currentRotation / 360)
const dynamicPossibleRotations = [
this.rotations[this.current] - (multiple * 360) - 360,
this.rotations[this.current] + (multiple * 360),
this.rotations[this.current] + (multiple * 360) + 360,
]
this.currentRotation = dynamicPossibleRotations.reduce((previous, current) => {
return (Math.abs(current - previousRotation) < Math.abs(previous - previousRotation) ? current : previous)
})
this.possibleRotations = dynamicPossibleRotations
},
items: items,
}
}
const wheelItems = [
{
"title": "Home Page",
"content": "This circle nav was built using <a class='underline' href='https://github.com/alpinejs/alpine' target='_blank'>AlpineJS</a>"
},
{
"title": "About Page",
"content": "It will continuously look for the shortest path to the next menu item"
},
{
"title": "Contact Page",
"content": "Hi from the about page content section! Nothing happening here though..."
},
{
"title": "Portfolio Page",
"content": "Time for some boring Lorem ipsum dolor sit amet...."
},
{
"title": "Some Other Page",
"content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
}
]