- enforce localhost:5174 for wheel campaign client URL in local development - lock vite dev server ports using strictPort on admin (5173) and client (5174) - sanitize .env.example and remove hardcoded staging domain - configure root dev orchestration and docker multi-stage build context
84 lines
3.2 KiB
Vue
84 lines
3.2 KiB
Vue
<script setup lang="ts">
|
|
import { toFarsiDigits, WheelDesignProps } from '@lucky-wheel/shared-types';
|
|
import { computed } from 'vue';
|
|
import { useWheelFromProps } from '../../composables/useWheelBase';
|
|
|
|
const props = defineProps<WheelDesignProps>();
|
|
const emit = defineEmits<{ (e: 'spin-finished'): void }>();
|
|
|
|
const { wheelStyle, pointerStyle, getSlicePath, getTextRotation, makePegs } = useWheelFromProps(
|
|
props,
|
|
() => emit('spin-finished'),
|
|
{ radius: 138, pegRadius: 142 },
|
|
);
|
|
|
|
const candyColors = ['#fb7185', '#fbbf24', '#34d399', '#60a5fa', '#c084fc', '#f472b6'];
|
|
|
|
const sprinkles = computed(() =>
|
|
makePegs(24, 142).map((peg, i) => ({
|
|
...peg,
|
|
color: candyColors[i % candyColors.length],
|
|
})),
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<div class="relative w-full max-w-[340px] xs:max-w-[380px] sm:max-w-[420px] aspect-square mx-auto select-none">
|
|
<div
|
|
class="absolute inset-0 rounded-full p-3.5 border-4 border-pink-300 shadow-[0_10px_0_#f9a8d4,0_16px_28px_rgba(244,114,182,0.35)]"
|
|
style="background: linear-gradient(180deg, #ffe4e6 0%, #fbcfe8 100%)"
|
|
>
|
|
<div class="w-full h-full rounded-full border-4 border-white relative overflow-hidden bg-rose-50">
|
|
<svg class="w-full h-full" :style="wheelStyle" viewBox="0 0 300 300">
|
|
<g v-for="(seg, idx) in segments" :key="seg.id">
|
|
<path
|
|
:d="getSlicePath(idx)"
|
|
:fill="seg.color || candyColors[idx % candyColors.length]"
|
|
stroke="#ffffff"
|
|
stroke-width="3"
|
|
/>
|
|
<g :transform="getTextRotation(idx)">
|
|
<text
|
|
x="150"
|
|
y="45"
|
|
text-anchor="middle"
|
|
fill="#ffffff"
|
|
font-size="11"
|
|
font-weight="900"
|
|
class="wheel-slice-text font-sans"
|
|
style="paint-order: stroke fill; stroke: rgba(0,0,0,0.2); stroke-width: 2px"
|
|
>
|
|
{{ toFarsiDigits(seg.title) }}
|
|
</text>
|
|
<image v-if="seg.image" :href="seg.image" x="138" y="54" width="24" height="24" />
|
|
</g>
|
|
</g>
|
|
|
|
<g v-for="(peg, pIdx) in sprinkles" :key="pIdx">
|
|
<circle :cx="peg.x" :cy="peg.y" r="4.2" fill="#fff" stroke="#f9a8d4" stroke-width="1.2" />
|
|
<circle :cx="peg.x" :cy="peg.y" r="2.6" :fill="peg.color" />
|
|
</g>
|
|
|
|
<circle cx="150" cy="150" r="32" fill="#fb7185" stroke="#fff" stroke-width="4" />
|
|
<circle cx="150" cy="150" r="20" fill="#fef08a" stroke="#f9a8d4" stroke-width="2" />
|
|
<text x="150" y="157" text-anchor="middle" font-size="16">🍬</text>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="absolute -top-4 left-1/2 -translate-x-1/2 z-20 pointer-events-none drop-shadow-[0_5px_0_#db2777]">
|
|
<div :style="pointerStyle" class="w-10 h-[48px] origin-[50%_14px] will-change-transform">
|
|
<svg width="40" height="48" viewBox="0 0 40 48" fill="none">
|
|
<path
|
|
d="M20 48L6 16C4 11 8 4 14 4H26C32 4 36 11 34 16L20 48Z"
|
|
fill="#fb7185"
|
|
stroke="#fff"
|
|
stroke-width="2.5"
|
|
/>
|
|
<circle cx="20" cy="14" r="5.5" fill="#fef08a" stroke="#fff" stroke-width="2" />
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|