M src/assets/base.css => src/assets/base.css +1 -1
@@ 8,7 8,7 @@ body {
}
#app {
- @apply flex justify-around items-center w-screen min-h-screen;
+ @apply flex justify-around items-center w-screen h-screen overflow-hidden;
}
button, input {
M src/composables/useRaffle.ts => src/composables/useRaffle.ts +1 -0
@@ 15,6 15,7 @@ export default function useRaffle() {
title: '',
date: new Date().toLocaleDateString('de'),
participants: [],
+ description: '',
})
const newParticipant = ref('')
M src/pages/Home.vue => src/pages/Home.vue +9 -2
@@ 17,12 17,12 @@ const {
} = useRaffle()
const showNewRaffleForm = $ref(false)
+const router = useRouter()
function startRaffle() {
if (!readyToRaffle) return
raffleStore.value.push(newRaffle.value)
- const router = useRouter()
router.push(`/raffle/${newRaffle.value.id}`)
}
</script>
@@ 47,12 47,19 @@ function startRaffle() {
<div class="text-xl" v-if="showNewRaffleForm">
<input
- class="w-full mb-16"
+ class="w-full mb-8"
placeholder="Amazing Raffle Title"
:class="{ 'border-transparent': newRaffle.title.length }"
v-model="newRaffle.title"
/>
+ <input
+ class="w-full mb-16"
+ placeholder="Some description"
+ :class="{ 'border-transparent': newRaffle.title.length }"
+ v-model="newRaffle.description"
+ />
+
<section>
<ul>
<li class="flex justify-between items-center mb-1" v-for="(p, i) in newRaffle.participants">
M src/pages/Raffle.vue => src/pages/Raffle.vue +70 -82
@@ 1,5 1,4 @@
<script setup lang="ts">
-import { onMounted } from 'vue'
import { useHead } from '@vueuse/head'
import { useStorage } from '@vueuse/core'
import { useRoute } from 'vue-router'
@@ 8,104 7,93 @@ const route = useRoute()
const raffleId = route.params.id as string
const raffleStore = useStorage<Raffle[]>('', [])
-const raffle = raffleStore.value.find(r => r.id === raffleId)
+const raffle = $computed(() => raffleStore.value.find(r => r.id === raffleId))
-const participants = $computed(() => {
+useHead({ title: raffle ? raffle.title : 'Lets Go' })
+
+const items = $computed(() => {
return raffle ? raffle.participants : []
})
-const amount = $computed(() => participants.length)
-let aligned = $ref(false) // used for animation
+let rollRot = $ref(0)
+const sliceRot = 360 / items.length
+const sliceSkew = sliceRot + 90
+const labelShift = 90 - sliceRot / 2
+const pieRot = $computed(() => `${(-90 + sliceRot / 2) + rollRot}deg`)
-function degrees(index: number) {
- if (!aligned) return 0
- return (360 / amount) * index
+function roll() {
+ if (rollRot) rollRot = 0
+ else {
+ const winner = Math.round(Math.random() * items.length)
+ rollRot = 3600 + sliceRot * winner
+ }
}
-
-useHead({ title: raffle ? raffle.title : 'Lets Go' })
-
-onMounted(() => {
- setTimeout(() => {
- aligned = true
- }, 100)
-})
</script>
<template>
- <div class="relative w-screen h-screen overflow-hidden flex justify-end items-center">
- <ol class="absolute top-0 -left-1/2 w-screen h-screen transition-transform duration-500 ease-in" :class="aligned ? 'aligned' : 'shifted'">
- <li v-for="(participant, i) in participants"
- class="slice"
- :style="`
- transform: rotate(${degrees(i)}deg);
- `"
- >
- <div>
- {{ participant }}bcdef ghijklmn
- </div>
- </li>
- <div class="absolute top-1/2 left-1/2 w-32 h-32 -ml-16 -mt-16 rounded-full bg-black"></div>
- </ol>
+ <div class="w-full h-full">
+ <div class="flex flex-col justify-between items-center w-1/2 h-full">
+ <header>
+ <h1 class="my-8 text-2xl">{{ raffle.title }}</h1>
+ </header>
+ <button @click="roll" class="text-4xl">{{ rollRot === 0 ? 'Roll!' : 'Reset' }}</button>
+ <footer>
+ <p class="my-4">Some raffle description</p>
+ </footer>
+ </div>
</div>
+ <ol
+ class="pie w-96 h-96 border-2 border-black rounded-full bg-white/10 overflow-hidden transition-transform"
+ :style="`transition-duration: ${rollRot ? 30 : 1}s`"
+ >
+ <li v-for="item,i in items"
+ class="slice"
+ :style="`transform: rotate(${sliceRot * i}deg) skewY(${sliceSkew}deg)`"
+ >
+ </li>
+ <li v-for="item,i in items"
+ class="label"
+ :style="`transform: rotate(${sliceRot * i + labelShift}deg)`"
+ >
+ {{ item }}
+ </li>
+ </ol>
</template>
<style scoped>
-.shifted {
- transform: translateX(-100%);
-}
-.aligned {
- transform: translateX(0);
-}
-.slice {
- --w: 50vw;
- --h: calc(6.283185307179586 * var(--w) / v-bind(amount));
+.pie {
position: absolute;
- width: var(--w);
- height: var(--h);
- left: 50%;
- top: 28%;
- padding: 0;
- text-align: right;
- transform-origin: left center;
- transition: transform 1s ease-out .4s;
- color: white;
- font-weight: bold;
- font-size: 2em;
-}
-.slice:nth-child(even) {
- color: black;
+ right: -12rem;
+ transform: rotate(v-bind(pieRot)) scale(3);
+ transition: transform 30s cubic-bezier(.38,.16,.67,.89);
}
-.slice::before, .slice::after {
- content: '';
- display: block;
- width: 0;
- height: 0;
- border-style: solid;
-}
-.slice::before {
- margin-bottom: -1px;
- border-width: 0 0 calc(var(--h) / 2) calc(var(--w) * .97);
- border-color: transparent transparent #0074D9 transparent;
-}
-.slice:nth-child(even)::before {
- border-color: transparent transparent #2ECC40 transparent;
-}
-.slice::after {
- border-width: 0 calc(var(--w) * .97) calc(var(--h) / 2) 0;
- border-color: transparent #0074D9 transparent transparent;
-}
-.slice:nth-child(even)::after {
- border-color: transparent #2ECC40 transparent transparent;
+.label {
+ position: absolute;
+ top: calc(50% - .5em);
+ left: 0;
+ width: 50%;
+ height: 1em;
+ padding-left: 1em;
+ line-height: 1em;
+ transform-origin: center right;
}
-
-.slice > div {
+.slice {
position: absolute;
- top: 0;
- bottom: 0;
- display: flex;
- justify-content: flex-end;
- align-items: center;
- width: 85%;
+ top: -50%;
+ right: -50%;
+ width: 100%;
height: 100%;
+ transform-origin: 0% 100%;
+ display: flex;
+ justify-content: flex-start;
+ align-items: flex-end;
+ border: 1px solid black;
}
+.slice:nth-child(n) { background-color: #555; }
+.slice:nth-child(2n) { background-color: #55A; }
+.slice:nth-child(3n) { background-color: #A55; }
+.slice:nth-child(4n) { background-color: #AA5; }
+.slice:nth-child(5n) { background-color: #A5A; }
+.slice:nth-child(6n) { background-color: #5AA; }
+.slice:nth-child(7n) { background-color: #999; }
</style>
M src/types.d.ts => src/types.d.ts +1 -0
@@ 4,6 4,7 @@ declare global {
title: string
date: string
participants: string[]
+ description: string
}
}