JSFiddle - React, Tailwind, and code Playground
by denvit
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
<div id="app">
<button @click="showFirst = !showFirst">
CHANGE
</button>
<circular v-if="showFirst" :value="value" @select="value = arguments[0]" :items="items"></circular>
<circular v-if="!showFirst" :value="45" @select="value = arguments[0]" :items="items"></circular>
</div>
CSS
.CircularSelect {
width: 250px;
height: 250px;
border-radius: 50%;
background: #e0e0e0;
padding: 24px;
position: relative;
cursor: pointer;
}
.CircularSelect__Inner {
position: relative;
height: 100%;
}
.CircularSelect__Pointer {
width: 1px;
height: 50%;
margin: 0 auto;
transform-origin: bottom center;
min-height: 0;
left: 0;
right: 0;
bottom: 0;
background: #027be3;
pointer-events: none;
}
.CircularSelect__Pointer__Bubble {
width: 10px;
height: 10px;
background: #027be3;
border-radius: 50%;
transform: translate(-50%, -50%);
}
.CircularSelect__Position {
position: absolute;
min-height: 32px;
width: 32px;
height: 32px;
font-size: 12px;
line-height: 32px;
margin: 0;
padding: 0;
transform: translate(-50%, -50%);
border-radius: 50%;
text-align: center;
cursor: pointer;
pointer-events: none;
}
.CircularSelect__Position--selected {
background-color: #027be3;
color: #ffffff;
}
.CircularSelect__Position--disabled {
color: #bfbfbf;
}
JavaScript
var circular = {
name: 'circular',
template: `<div class="CircularSelect" @mousedown="dragStart" @mouseup="dragStop" @mouseleave="dragStop" @touchstart="dragStart" @touchend="dragStop">
<div class="CircularSelect__Inner" ref="circle">
<div ref="pointer" class="CircularSelect__Pointer">
<div v-show="points.indexOf(value) === -1" class="CircularSelect__Pointer__Bubble"></div>
</div>
<div
v-for="(item, index) in items" class="CircularSelect__Position"
v-if="points.indexOf(item.id) >= 0"
:class="{ 'CircularSelect__Position--selected': item.id === value, 'CircularSelect__Position--disabled': item.disabled }"
:style="itemStyle(index)" @click="select(item)">
{{ item.title }}
</div>
</div>
</div>`,
props: {
value: {
required: true,
},
items: {
type: Array,
required: true,
},
keyPoints: {
type: Array,
},
},
data() {
return {
circleRadius: 0,
centerPosition: {
top: 0,
left: 0,
},
};
},
computed: {
itemAngle() {
return 360 / this.items.length;
},
points() {
if (this.keyPoints) {
return this.keyPoints;
}
return this.items.map(item => item.id);
},
},
mounted() {
this.refresh();
},
updated() {
//this.refresh();
},
methods: {
refresh() {
this.circleRadius = this.$refs.circle.clientWidth / 2;
this.$refs.pointer.style.transform = `rotate(${this.itemAngle * this.items.findIndex(item => item.id === this.value)}deg)`;
},
itemStyle(index) {
let angle = index * this.itemAngle - 90;
return {
top: 50 + 50 * Math.sin(angle * Math.PI / 180) + '%',
left: 50 + 50 * Math.cos(angle * Math.PI / 180) + '%',
};
},
dragStart(event) {
...