120 lines
5.5 KiB
Vue
120 lines
5.5 KiB
Vue
<script setup lang="ts">
|
|
import { clamp } from '#shared/general.util';
|
|
import { MAIN_STATS, mainStatTexts, type CharacterConfig } from '~/types/character';
|
|
|
|
const { config } = defineProps<{
|
|
config: CharacterConfig,
|
|
}>();
|
|
|
|
const dragger = useTemplateRef<HTMLElement | null>('dragger'), items = useTemplateRef<HTMLElement[] | null>('items');
|
|
const position = ref(0), id = ref<number>(0);
|
|
const dragging = ref(false), offset = ref(0);
|
|
|
|
const dragend = () => {
|
|
window.removeEventListener('mousemove', dragmove);
|
|
window.removeEventListener('mouseup', dragend);
|
|
|
|
dragging.value = false;
|
|
};
|
|
const dragmove = (e: MouseEvent) => {
|
|
const box = dragger.value!.getBoundingClientRect();
|
|
offset.value = clamp(offset.value - e.movementX, 0, (320+32+2) * 16);
|
|
if(dragger.value) dragger.value.scrollLeft = offset.value;
|
|
};
|
|
const dragstart = () => {
|
|
window.addEventListener('mousemove', dragmove);
|
|
window.addEventListener('mouseup', dragend);
|
|
|
|
dragging.value = true;
|
|
};
|
|
const wheel = (e: WheelEvent) => {
|
|
if(dragging.value) return;
|
|
|
|
const box = dragger.value!.getBoundingClientRect();
|
|
offset.value = clamp(offset.value + e.deltaY, 0, (320+32+2) * 16);
|
|
if(dragger.value) dragger.value.scrollLeft = offset.value;
|
|
}
|
|
onMounted(() => {
|
|
|
|
dragger.value?.addEventListener('mousedown', dragstart);
|
|
dragger.value?.addEventListener('wheel', wheel);
|
|
|
|
transition(1, 0);
|
|
});
|
|
onUnmounted(() => {
|
|
dragger.value?.removeEventListener('mousedown', dragstart);
|
|
dragger.value?.removeEventListener('wheel', wheel);
|
|
});
|
|
function transition(from: number, to: number)
|
|
{
|
|
if(!items.value || from === to)
|
|
return;
|
|
|
|
position.value = to;
|
|
items.value![to].style.visibility = 'visible';
|
|
items.value![from].style.opacity = '0';
|
|
items.value![to].style.opacity = '1';
|
|
|
|
for(let i = 0; i < MAIN_STATS.length; i++)
|
|
{
|
|
if(i < to)
|
|
items.value![i].style.top = `-25%`;
|
|
else
|
|
items.value![i].style.top = `25%`;
|
|
}
|
|
items.value![to].style.top = `0%`;
|
|
|
|
clearTimeout(id.value);
|
|
//@ts-ignore
|
|
id.value = setTimeout(() => {
|
|
items.value![from].style.visibility = 'hidden';
|
|
}, 200);
|
|
}
|
|
</script>
|
|
|
|
<!-- <template>
|
|
<div class="flex flex-1 flex-row justify-start w-full max-h-full h-full overflow-hidden gap-8 relative">
|
|
<div class="flex flex-col gap-3 relative items-center">
|
|
<span v-for="(stat, i) of MAIN_STATS" :value="stat" class="block w-2.5 h-2.5 m-px outline outline-1 outline-transparent
|
|
hover:outline-light-70 dark:hover:outline-dark-70 rounded-full bg-light-40 dark:bg-dark-40 cursor-pointer" @click="() => transition(position, i)"></span>
|
|
<span :style="{ 'top': position * 1.5 + 'em' }" :data-text="mainStatTexts[MAIN_STATS[position]]" class="rounded-full w-3 h-3 bg-accent-blue absolute transition-[top]
|
|
after:content-[attr(data-text)] after:absolute after:-top-2 after:left-4 after:p-px after:bg-light-0 dark:after:bg-dark-0"></span>
|
|
</div>
|
|
<div class="flex flex-1 flex-col justify-center relative cursor-grab" ref="dragger" :class="{ 'cursor-grabbing': dragging }">
|
|
<div v-for="(stat) of MAIN_STATS" :value="stat" class="flex-1 transition-[opacity,transform] items-center hidden absolute" ref="items" :style="{ 'left': `${-offset}%` }">
|
|
<div class="flex flex-row overflow-x-auto items-center w-full gap-4">
|
|
<div class="w-96 flex flex-col gap-4 justify-between" v-for="(level, i) of config.training[stat]">
|
|
<template v-for="(option) of level">
|
|
<slot :stat="stat" :level="i" :option="option"></slot>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template> -->
|
|
|
|
<template>
|
|
<div class="w-full h-full flex gap-8 max-w-full relative">
|
|
<div class="flex flex-col gap-3 relative items-center">
|
|
<span v-for="(stat, i) of MAIN_STATS" :value="stat" class="block w-2.5 h-2.5 m-px outline outline-1 outline-transparent
|
|
hover:outline-light-70 dark:hover:outline-dark-70 rounded-full bg-light-40 dark:bg-dark-40 cursor-pointer" @click="() => transition(position, i)"></span>
|
|
<span :style="{ 'top': position * 1.5 + 'em' }" :data-text="mainStatTexts[MAIN_STATS[position]]" class="rounded-full w-3 h-3 bg-accent-blue absolute transition-[top]
|
|
after:content-[attr(data-text)] after:absolute after:-top-2 after:left-4 after:p-px after:bg-light-0 dark:after:bg-dark-0"></span>
|
|
</div>
|
|
<div class="absolute top-0 left-24 z-10">
|
|
<slot name="addin" :stat="MAIN_STATS[position]"></slot>
|
|
</div>
|
|
<div ref="dragger" class="flex flex-col gap-4 pb-4 cursor-grab active:cursor-grabbing select-none overflow-hidden h-full w-full relative">
|
|
<div v-for="(stat, name) in config.training" class="flex flex-1 gap-4 items-center absolute h-full z-0" ref="items" :style="{ 'opacity': '0', 'visibility': 'hidden', 'transition': 'opacity 200ms ease-in-out, top 200ms ease-in-out' }">
|
|
<div v-for="(options, level) in stat" class="flex-shrink-0 w-80">
|
|
<div class="space-y-2" @mousedown.stop>
|
|
<template v-for="(option, i) in options">
|
|
<slot :stat="name" :level="level" :index="i" :option="option"></slot>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template> |