Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@zohaibtariq/vuewinwheel

zohaibtariq97MIT1.0.2TypeScript support: included

install with npm i @zohaibtariq/vuewinwheel

vue, vue3, spin, wheel, winwheel, win wheel, vue win wheel, vue 3 win wheel, spinner, game, lucky draw, prize, animation, component, ui, interactive, canvas, roulette, gamification, vue, vue3, spin, wheel, spinner, game, lucky draw, prize, animation, component, ui, canvas, random, chance, win, reward, rotation, motion, spinning animation, gamble, entertainment, fun, wheel of fortune, digital game, winning element, reward system, gamified experience, gamified user interface, randomized content, randomized rewards, virtual prize wheel, online game, decision making, randomized outcome, user entertainment, gamification element, web animation, virtual game, game component, reward distribution

readme

Vue 3 Win Wheel

This is based on library winwheel.js, all examples are in vue 3 composition api, it helps you programatically stop at any segment of your criteria just pass segment number in stopAt variable.

install with

npm i @zohaibtariq/vuewinwheel

npmjs

@zohaibtariq/vuewinwheel

Vue 3 Winwheel Examples


Basic code wheel

<template>
  <VueWinWheel ref="winWheel" :options="allOptions" :canvasWidth="canvasWidth" :canvasHeight="canvasHeight" :stopAt="stopAt" />
  <br/>
  <br/>
  <br/>
  <button @click="spinWinWheel">Random Spin</button>
  <button @click="spinWinWheelStopAt">Defined Spin</button>
</template>

<script setup>
import {VueWinWheel} from '@zohaibtariq/vuewinwheel'
import '@zohaibtariq/vuewinwheel/dist/style.css';
import { ref, onMounted } from 'vue';

const winWheel = ref();

const spinWinWheel = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spin() // winWheelInstances is only required for the (2 part wheel)
};

const spinWinWheelStopAt = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spinAndStopAt(stopAt)
};

const canvasWidth = 500; // set custom canvas width
const canvasHeight = 500; // set custom canvas height
const stopAt = 3; // Segment at which you want to stop at

let spinningAudio = new Audio('./spinning.mp3'); // update this with your own tik sound
let winnerAudio = new Audio('./winner.mp3'); // update this with your own win sound

// Called when the animation has finished.
function spinStopped(indicatedSegment)
{
  playWinningSound()
  // Display different message if win/lose/bankrupt.
  // this must need to be updated according to your segment text
  if (indicatedSegment.text == 'LOOSE TURN')
    alert('Sorry but you loose a turn.');
  else if (indicatedSegment.text == 'BANKRUPT')
    alert('Oh no, you have gone BANKRUPT!');
  else
    alert("You have won " + indicatedSegment.text);
}

function eachSegmentSpin()
{
  playSpinningSound()
}

let spinStartTime;

function playSpinningSound()
{
  if(!spinStartTime){
    spinStartTime = Date.now(); // this must start when spin starts
  }
  spinningAudio.pause();
  const spinEndTime = Date.now();
  const diffInSpinTime = spinEndTime - spinStartTime
  // do not play this sound if animation time is exceeded
  if(options?.animation?.duration === undefined || diffInSpinTime < ((options.animation.duration * 1000)/1.4)) { // if you observe wheel stop but tik sound continues to play increase value of 1.4 to gradually a higher digit number like 1.5, 1.6 what ever suites you, animation duration and spin setting effects it
    spinningAudio.currentTime = 0;
    spinningAudio.play();
  }
}

function playWinningSound()
{
  winnerAudio.pause();
  winnerAudio.currentTime = 0;
  winnerAudio.play();
}

const options = { // basic_code_wheel
  'numSegments'  : 8,            // Number of segments
  'outerRadius'  : 212,        // The size of the wheel.
  'centerX'      : 217,        // Used to position on the background correctly.
  'centerY'      : 219,
  'textFontSize' : 28,        // Font size.
  'segments'     :            // Definition of all the segments.
      [
        {'fillStyle' : '#eae56f', 'text' : 'Prize 1'},
        {'fillStyle' : '#89f26e', 'text' : 'Prize 2'},
        {'fillStyle' : '#7de6ef', 'text' : 'Prize 3'},
        {'fillStyle' : '#e7706f', 'text' : 'Prize 4'},
        {'fillStyle' : '#eae56f', 'text' : 'Prize 5'},
        {'fillStyle' : '#89f26e', 'text' : 'Prize 6'},
        {'fillStyle' : '#7de6ef', 'text' : 'Prize 7'},
        {'fillStyle' : '#e7706f', 'text' : 'Prize 8'}
      ],
  'animation' :                // Definition of the animation
      {
        'type'     : 'spinToStop',
        'duration' : 5,
        'spins'    : 8,
        'callbackFinished' : spinStopped,  // Function to call when the spinning has stopped.
        'callbackSound'    : eachSegmentSpin,   // Called when the tick sound is to be played.
      }
};

const allOptions = {
  ...options,
  outerRadius: (canvasWidth / 2) - (options.lineWidth || 1)
};

onMounted(() => {
  allOptions.outerRadius = (canvasWidth / 2) - (options.lineWidth || 1);
});

</script>

<style scoped>
/* Define your css here inspect dom to see what ids and classes are available */
</style>

Wheel of fortune

<template>
  <VueWinWheel ref="winWheel" :options="allOptions" :canvasWidth="canvasWidth" :canvasHeight="canvasHeight" :stopAt="stopAt" />
  <br/>
  <br/>
  <br/>
  <button @click="spinWinWheel">Random Spin</button>
  <button @click="spinWinWheelStopAt">Defined Spin</button>
</template>

<script setup>
import {VueWinWheel} from '@zohaibtariq/vuewinwheel'
import '@zohaibtariq/vuewinwheel/dist/style.css';
import { ref, onMounted } from 'vue';

const winWheel = ref();

const spinWinWheel = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spin() // winWheelInstances is only required for the (2 part wheel)
};

const spinWinWheelStopAt = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spinAndStopAt(stopAt)
};

const canvasWidth = 500; // set custom canvas width
const canvasHeight = 500; // set custom canvas height
const stopAt = 3; // Segment at which you want to stop at

let spinningAudio = new Audio('./spinning.mp3'); // update this with your own tik sound
let winnerAudio = new Audio('./winner.mp3'); // update this with your own win sound

// Called when the animation has finished.
function spinStopped(indicatedSegment)
{
  playWinningSound()
  // Display different message if win/lose/bankrupt.
  // this must need to be updated according to your segment text
  if (indicatedSegment.text == 'LOOSE TURN')
    alert('Sorry but you loose a turn.');
  else if (indicatedSegment.text == 'BANKRUPT')
    alert('Oh no, you have gone BANKRUPT!');
  else
    alert("You have won " + indicatedSegment.text);
}

function eachSegmentSpin()
{
  playSpinningSound()
}

let spinStartTime;

function playSpinningSound()
{
  if(!spinStartTime){
    spinStartTime = Date.now(); // this must start when spin starts
  }
  spinningAudio.pause();
  const spinEndTime = Date.now();
  const diffInSpinTime = spinEndTime - spinStartTime
  // do not play this sound if animation time is exceeded
  if(options?.animation?.duration === undefined || diffInSpinTime < ((options.animation.duration * 1000)/1.4)) { // if you observe wheel stop but tik sound continues to play increase value of 1.4 to gradually a higher digit number like 1.5, 1.6 what ever suites you, animation duration and spin setting effects it
    spinningAudio.currentTime = 0;
    spinningAudio.play();
  }
}

function playWinningSound()
{
  winnerAudio.pause();
  winnerAudio.currentTime = 0;
  winnerAudio.play();
}

const options = { // wheel_of_fortune
  'outerRadius'     : 212,        // Set outer radius so wheel fits inside the background.
  'innerRadius'     : 75,         // Make wheel hollow so segments don't go all way to center.
  'textFontSize'    : 24,         // Set default font size for the segments.
  'textOrientation' : 'vertical', // Make text vertical so goes down from the outside of wheel.
  'textAlignment'   : 'outer',    // Align text to outside of wheel.
  'numSegments'     : 24,         // Specify number of segments.
  'segments'        :             // Define segments including colour and text.
      [                               // font size and text colour overridden on bankrupt segments.
        {'fillStyle' : '#ee1c24', 'text' : '300'},
        {'fillStyle' : '#3cb878', 'text' : '450'},
        {'fillStyle' : '#f6989d', 'text' : '600'},
        {'fillStyle' : '#00aef0', 'text' : '750'},
        {'fillStyle' : '#f26522', 'text' : '500'},
        {'fillStyle' : '#000000', 'text' : 'BANKRUPT', 'textFontSize' : 16, 'textFillStyle' : '#ffffff'},
        {'fillStyle' : '#e70697', 'text' : '3000'},
        {'fillStyle' : '#fff200', 'text' : '600'},
        {'fillStyle' : '#f6989d', 'text' : '700'},
        {'fillStyle' : '#ee1c24', 'text' : '350'},
        {'fillStyle' : '#3cb878', 'text' : '500'},
        {'fillStyle' : '#f26522', 'text' : '800'},
        {'fillStyle' : '#a186be', 'text' : '300'},
        {'fillStyle' : '#fff200', 'text' : '400'},
        {'fillStyle' : '#00aef0', 'text' : '650'},
        {'fillStyle' : '#ee1c24', 'text' : '1000'},
        {'fillStyle' : '#f6989d', 'text' : '500'},
        {'fillStyle' : '#f26522', 'text' : '400'},
        {'fillStyle' : '#3cb878', 'text' : '900'},
        {'fillStyle' : '#000000', 'text' : 'BANKRUPT', 'textFontSize' : 16, 'textFillStyle' : '#ffffff'},
        {'fillStyle' : '#a186be', 'text' : '600'},
        {'fillStyle' : '#fff200', 'text' : '700'},
        {'fillStyle' : '#00aef0', 'text' : '800'},
        {'fillStyle' : '#ffffff', 'text' : 'LOOSE TURN', 'textFontSize' : 12}
      ],
  'animation' :           // Specify the animation to use.
      {
        'type'     : 'spinToStop',
        'duration' : 3,
        'spins'    : 10,
        'callbackFinished' : spinStopped,  // Function to call when the spinning has stopped.
        'callbackSound'    : eachSegmentSpin,   // Called when the tick sound is to be played.
        'soundTrigger'     : 'pin'        // Specify pins are to trigger the sound.
      },
  'pins' :                // Turn pins on.
      {
        'number'     : 24,
        'fillStyle'  : 'silver',
        'outerRadius': 4,
      }
};

const allOptions = {
  ...options,
  outerRadius: (canvasWidth / 2) - (options.lineWidth || 1)
};

onMounted(() => {
  allOptions.outerRadius = (canvasWidth / 2) - (options.lineWidth || 1);
});

</script>

<style scoped>
/* Define your css here inspect dom to see what ids and classes are available */
</style>

Basic image wheel

<template>
  <VueWinWheel ref="winWheel" :options="allOptions" :canvasWidth="canvasWidth" :canvasHeight="canvasHeight" :stopAt="stopAt" />
  <br/>
  <br/>
  <br/>
  <button @click="spinWinWheel">Random Spin</button>
  <button @click="spinWinWheelStopAt">Defined Spin</button>
</template>

<script setup>
import {VueWinWheel} from '@zohaibtariq/vuewinwheel'
import '@zohaibtariq/vuewinwheel/dist/style.css';
import { ref, onMounted } from 'vue';

const winWheel = ref();

const spinWinWheel = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spin() // winWheelInstances is only required for the (2 part wheel)
};

const spinWinWheelStopAt = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spinAndStopAt(stopAt)
};

const canvasWidth = 500; // set custom canvas width
const canvasHeight = 500; // set custom canvas height
const stopAt = 3; // Segment at which you want to stop at

let spinningAudio = new Audio('./spinning.mp3'); // update this with your own tik sound
let winnerAudio = new Audio('./winner.mp3'); // update this with your own win sound

// Called when the animation has finished.
function spinStopped(indicatedSegment)
{
  playWinningSound()
  // Display different message if win/lose/bankrupt.
  // this must need to be updated according to your segment text
  if (indicatedSegment.text == 'LOOSE TURN')
    alert('Sorry but you loose a turn.');
  else if (indicatedSegment.text == 'BANKRUPT')
    alert('Oh no, you have gone BANKRUPT!');
  else
    alert("You have won " + indicatedSegment.text);
}

function eachSegmentSpin()
{
  playSpinningSound()
}

let spinStartTime;

function playSpinningSound()
{
  if(!spinStartTime){
    spinStartTime = Date.now(); // this must start when spin starts
  }
  spinningAudio.pause();
  const spinEndTime = Date.now();
  const diffInSpinTime = spinEndTime - spinStartTime
  // do not play this sound if animation time is exceeded
  if(options?.animation?.duration === undefined || diffInSpinTime < ((options.animation.duration * 1000)/1.4)) { // if you observe wheel stop but tik sound continues to play increase value of 1.4 to gradually a higher digit number like 1.5, 1.6 what ever suites you, animation duration and spin setting effects it
    spinningAudio.currentTime = 0;
    spinningAudio.play();
  }
}

function playWinningSound()
{
  winnerAudio.pause();
  winnerAudio.currentTime = 0;
  winnerAudio.play();
}

const options = { // basic_image_wheel
  'numSegments'       : 4,            // Specify number of segments.
  'outerRadius'       : 150,          // Set outer radius so wheel fits inside the background.
  'drawMode'          : 'image',      // drawMode must be set to image.
  'imageSrc'          : "./wheel-colored.png",
  'drawText'          : true,         // Need to set this true if want code-drawn text on image wheels.
  'textFontSize'      : 24,
  'textOrientation'   : 'curved',        // Note use of curved text.
  'textDirection'     : 'reversed',    // Set other text options as desired.
  'textAlignment'     : 'outer',
  'textMargin'        : 5,
  'textFontFamily'    : 'monospace',
  'textStrokeStyle'   : 'black',
  'textLineWidth'     : 2,
  'textFillStyle'     : 'white',
  'segments'     :                // Define segments.
      [
        {'text' : 'T-55 Vampire'},
        {'text' : 'P-40 Kittyhawk'},
        {'text' : 'North American Harvard'},
        {'text' : 'L-39C Albatross'}
      ],
  'animation' :                   // Specify the animation to use.
      {
        'type'     : 'spinToStop',
        'duration' : 5,             // Duration in seconds.
        'spins'    : 8,             // Number of complete spins.
        'callbackFinished' : spinStopped
      }
};

const allOptions = {
  ...options,
  outerRadius: (canvasWidth / 2) - (options.lineWidth || 1)
};

onMounted(() => {
  allOptions.outerRadius = (canvasWidth / 2) - (options.lineWidth || 1);
});

</script>

<style scoped>
/* Define your css here inspect dom to see what ids and classes are available */
</style>

One image per segment

<template>
  <VueWinWheel ref="winWheel" :options="allOptions" :canvasWidth="canvasWidth" :canvasHeight="canvasHeight" :stopAt="stopAt" />
  <br/>
  <br/>
  <br/>
  <button @click="spinWinWheel">Random Spin</button>
  <button @click="spinWinWheelStopAt">Defined Spin</button>
</template>

<script setup>
import {VueWinWheel} from '@zohaibtariq/vuewinwheel'
import '@zohaibtariq/vuewinwheel/dist/style.css';
import { ref, onMounted } from 'vue';

const winWheel = ref();

const spinWinWheel = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spin() // winWheelInstances is only required for the (2 part wheel)
};

const spinWinWheelStopAt = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spinAndStopAt(stopAt)
};

const canvasWidth = 500; // set custom canvas width
const canvasHeight = 500; // set custom canvas height
const stopAt = 3; // Segment at which you want to stop at

let spinningAudio = new Audio('./spinning.mp3'); // update this with your own tik sound
let winnerAudio = new Audio('./winner.mp3'); // update this with your own win sound

// Called when the animation has finished.
function spinStopped(indicatedSegment)
{
  playWinningSound()
  // Display different message if win/lose/bankrupt.
  // this must need to be updated according to your segment text
  if (indicatedSegment.text == 'LOOSE TURN')
    alert('Sorry but you loose a turn.');
  else if (indicatedSegment.text == 'BANKRUPT')
    alert('Oh no, you have gone BANKRUPT!');
  else
    alert("You have won " + indicatedSegment.text);
}

function eachSegmentSpin()
{
  playSpinningSound()
}

let spinStartTime;

function playSpinningSound()
{
  if(!spinStartTime){
    spinStartTime = Date.now(); // this must start when spin starts
  }
  spinningAudio.pause();
  const spinEndTime = Date.now();
  const diffInSpinTime = spinEndTime - spinStartTime
  // do not play this sound if animation time is exceeded
  if(options?.animation?.duration === undefined || diffInSpinTime < ((options.animation.duration * 1000)/1.4)) { // if you observe wheel stop but tik sound continues to play increase value of 1.4 to gradually a higher digit number like 1.5, 1.6 what ever suites you, animation duration and spin setting effects it
    spinningAudio.currentTime = 0;
    spinningAudio.play();
  }
}

function playWinningSound()
{
  winnerAudio.pause();
  winnerAudio.currentTime = 0;
  winnerAudio.play();
}

const options = { // one_image_per_segment
  'numSegments'       : 8,                // Specify number of segments.
  'outerRadius'       : 200,              // Set outer radius so wheel fits inside the background.
  'drawText'          : true,             // Code drawn text can be used with segment images.
  'textFontSize'      : 16,                // Set text options as desired.
  'textOrientation'   : 'curved',
  'textAlignment'     : 'inner',
  'textMargin'        : 90,
  'textFontFamily'    : 'monospace',
  'textStrokeStyle'   : 'black',
  'textLineWidth'     : 3,
  'textFillStyle'     : 'white',
  'drawMode'          : 'segmentImage',    // Must be segmentImage to draw wheel using one image per segemnt.
  'segments'          :                    // Define segments including image and text.
      [
        {'image' : './tom.png',  'text' : 'Jane'}, // please update tom.png with your image path
        {'image' : './tom.png',   'text' : 'Tom'},
        {'image' : './tom.png',  'text' : 'Mary'},
        {'image' : './tom.png',  'text' : 'Alex'},
        {'image' : './tom.png', 'text' : 'Sarah'},
        {'image' : './tom.png', 'text' : 'Bruce'},
        {'image' : './tom.png',  'text' : 'Rose'},
        {'image' : './tom.png', 'text' : 'Steve'}
      ],
  'animation' :           // Specify the animation to use.
      {
        'type'     : 'spinToStop',
        'duration' : 5,
        'spins'    : 8,
        'callbackFinished' : spinStopped
      }
}

const allOptions = {
  ...options,
  outerRadius: (canvasWidth / 2) - (options.lineWidth || 1)
};

onMounted(() => {
  allOptions.outerRadius = (canvasWidth / 2) - (options.lineWidth || 1);
});

</script>

<style scoped>
/* Define your css here inspect dom to see what ids and classes are available */
</style>

Hollow wheel

<template>
  <VueWinWheel ref="winWheel" :options="allOptions" :canvasWidth="canvasWidth" :canvasHeight="canvasHeight" :stopAt="stopAt" />
  <br/>
  <br/>
  <br/>
  <button @click="spinWinWheel">Random Spin</button>
  <button @click="spinWinWheelStopAt">Defined Spin</button>
</template>

<script setup>
import {VueWinWheel} from '@zohaibtariq/vuewinwheel'
import '@zohaibtariq/vuewinwheel/dist/style.css';
import { ref, onMounted } from 'vue';

const winWheel = ref();

const spinWinWheel = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spin() // winWheelInstances is only required for the (2 part wheel)
};

const spinWinWheelStopAt = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spinAndStopAt(stopAt)
};

const canvasWidth = 500; // set custom canvas width
const canvasHeight = 500; // set custom canvas height
const stopAt = 3; // Segment at which you want to stop at

let spinningAudio = new Audio('./spinning.mp3'); // update this with your own tik sound
let winnerAudio = new Audio('./winner.mp3'); // update this with your own win sound

// Called when the animation has finished.
function spinStopped(indicatedSegment)
{
  playWinningSound()
  // Display different message if win/lose/bankrupt.
  // this must need to be updated according to your segment text
  if (indicatedSegment.text == 'LOOSE TURN')
    alert('Sorry but you loose a turn.');
  else if (indicatedSegment.text == 'BANKRUPT')
    alert('Oh no, you have gone BANKRUPT!');
  else
    alert("You have won " + indicatedSegment.text);
}

function eachSegmentSpin()
{
  playSpinningSound()
}

let spinStartTime;

function playSpinningSound()
{
  if(!spinStartTime){
    spinStartTime = Date.now(); // this must start when spin starts
  }
  spinningAudio.pause();
  const spinEndTime = Date.now();
  const diffInSpinTime = spinEndTime - spinStartTime
  // do not play this sound if animation time is exceeded
  if(options?.animation?.duration === undefined || diffInSpinTime < ((options.animation.duration * 1000)/1.4)) { // if you observe wheel stop but tik sound continues to play increase value of 1.4 to gradually a higher digit number like 1.5, 1.6 what ever suites you, animation duration and spin setting effects it
    spinningAudio.currentTime = 0;
    spinningAudio.play();
  }
}

function playWinningSound()
{
  winnerAudio.pause();
  winnerAudio.currentTime = 0;
  winnerAudio.play();
}

const options = { // hollow_wheel
  'numSegments'   : 16,   // Specify number of segments.
  'outerRadius'   : 212,  // Set radius to so wheel fits the background.
  'innerRadius'   : 120,  // Set inner radius to make wheel hollow.
  'textFontSize'  : 16,   // Set font size accordingly.
  'textMargin'    : 0,    // Take out default margin.
  'segments'      :       // Define segments including colour and text.
      [
        {'fillStyle' : '#eae56f', 'text' : 'Prize 1'},
        {'fillStyle' : '#89f26e', 'text' : 'Prize 2'},
        {'fillStyle' : '#7de6ef', 'text' : 'Prize 3'},
        {'fillStyle' : '#e7706f', 'text' : 'Prize 4'},
        {'fillStyle' : '#eae56f', 'text' : 'Prize 5'},
        {'fillStyle' : '#89f26e', 'text' : 'Prize 6'},
        {'fillStyle' : '#7de6ef', 'text' : 'Prize 7'},
        {'fillStyle' : '#e7706f', 'text' : 'Prize 8'},
        {'fillStyle' : '#eae56f', 'text' : 'Prize 9'},
        {'fillStyle' : '#89f26e', 'text' : 'Prize 10'},
        {'fillStyle' : '#7de6ef', 'text' : 'Prize 11'},
        {'fillStyle' : '#e7706f', 'text' : 'Prize 12'},
        {'fillStyle' : '#eae56f', 'text' : 'Prize 13'},
        {'fillStyle' : '#89f26e', 'text' : 'Prize 14'},
        {'fillStyle' : '#7de6ef', 'text' : 'Prize 15'},
        {'fillStyle' : '#e7706f', 'text' : 'Prize 16'}
      ],
  'animation' :           // Define spin to stop animation.
      {
        'type'     : 'spinToStop',
        'duration' : 5,
        'spins'    : 8,
        'callbackFinished' : spinStopped
      }
};

const allOptions = {
  ...options,
  outerRadius: (canvasWidth / 2) - (options.lineWidth || 1)
};

onMounted(() => {
  allOptions.outerRadius = (canvasWidth / 2) - (options.lineWidth || 1);
});

</script>

<style scoped>
/* Define your css here inspect dom to see what ids and classes are available */
</style>

2 Part Wheel

<template>
  <VueWinWheel ref="winWheel" :options="allOptions" :canvasWidth="canvasWidth" :canvasHeight="canvasHeight" :stopAt="stopAt" />
  <br/>
  <br/>
  <br/>
  <button @click="spinWinWheel">Random Spin</button>
  <button @click="spinWinWheelStopAt">Defined Spin</button>
</template>

<script setup>
import {VueWinWheel} from '@zohaibtariq/vuewinwheel'
import '@zohaibtariq/vuewinwheel/dist/style.css';
import { ref, onMounted } from 'vue';

const winWheel = ref();

const spinWinWheel = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spin(winWheelInstances) // winWheelInstances is only required for the (2 part wheel)
};

const spinWinWheelStopAt = () => {
  spinStartTime = Date.now() // if you miss this sound will not play after first run
  if(winWheel.value)
    winWheel.value.spinAndStopAt(stopAt, winWheelInstances)
};

const canvasWidth = 500; // set custom canvas width
const canvasHeight = 500; // set custom canvas height
const stopAt = 3; // Segment at which you want to stop at

let spinningAudio = new Audio('./spinning.mp3'); // update this with your own tik sound
let winnerAudio = new Audio('./winner.mp3'); // update this with your own win sound

// Called when the animation has finished.
function spinStopped(indicatedSegment)
{
  playWinningSound()
  // Display different message if win/lose/bankrupt.
  // this must need to be updated according to your segment text
  if (indicatedSegment.text == 'LOOSE TURN')
    alert('Sorry but you loose a turn.');
  else if (indicatedSegment.text == 'BANKRUPT')
    alert('Oh no, you have gone BANKRUPT!');
  else
    alert("You have won " + indicatedSegment.text);
}

function eachSegmentSpin()
{
  playSpinningSound()
}

let spinStartTime;

function playSpinningSound()
{
  if(!spinStartTime){
    spinStartTime = Date.now(); // this must start when spin starts
  }
  spinningAudio.pause();
  const spinEndTime = Date.now();
  const diffInSpinTime = spinEndTime - spinStartTime
  // do not play this sound if animation time is exceeded
  if(options?.animation?.duration === undefined || diffInSpinTime < ((options.animation.duration * 1000)/1.4)) { // if you observe wheel stop but tik sound continues to play increase value of 1.4 to gradually a higher digit number like 1.5, 1.6 what ever suites you, animation duration and spin setting effects it
    spinningAudio.currentTime = 0;
    spinningAudio.play();
  }
}

function playWinningSound()
{
  winnerAudio.pause();
  winnerAudio.currentTime = 0;
  winnerAudio.play();
}

const innerWheelOptions = { // two_part_wheel
  'numSegments' : 4,
  'outerRadius' : 110,        // Set the outer radius to make the wheel smaller than the outer wheel.
  'segments': [
    {'fillStyle' : '#eae56f', 'text' : 'Inner 1'},
    {'fillStyle' : '#89f26e', 'text' : 'Inner 2'},
    {'fillStyle' : '#7de6ef', 'text' : 'Inner 3'},
    {'fillStyle' : '#e7706f', 'text' : 'Inner 4'}
  ]
};

const outerWheelOptions = {
  'numSegments': 8,
  'textMargin' : 0,
  'outerRadius' : 210,
  'innerRadius' : 110,    // Set inner radius to the size of the inner wheel since the inner part of the wheel
  'segments': [           //   is being drawn by the inner wheel we don't need to draw there.
    {'fillStyle' : '#8C8A42', 'text' : 'Outer 1'},
    {'fillStyle' : '#F2F0A8', 'text' : 'Outer 2'},
    {'fillStyle' : '#519142', 'text' : 'Outer 3'},
    {'fillStyle' : '#B7F7A8', 'text' : 'Outer 4'},
    {'fillStyle' : '#4B898F', 'text' : 'Outer 5'},
    {'fillStyle' : '#B1EFF5', 'text' : 'Outer 6'},
    {'fillStyle' : '#8A4342', 'text' : 'Outer 7'},
    {'fillStyle' : '#F0A9A8', 'text' : 'Outer 8'}
  ],
  'animation':
      {
        'type': 'spinToStop',                     // Define animation more or less as normal, except for the callbackAfter().
        'duration': 5,
        'spins': 5,
        'easing': 'Power3.easeOut',
        'callbackAfter' : drawInnerWheel,     // Call back after each frame of the animation a function we can draw the inner wheel from.
        'callbackFinished': alertPrize
      }
};

// This function is called after the outer wheel has drawn during the animation.
function drawInnerWheel()
{
  // Update the rotationAngle of the innerWheel to match that of the outer wheel - this is a big part of what
  // links them to appear as one 2-part wheel. Call the draw function passing false so the outer wheel is not wiped.
  innerWinWheelInstance.rotationAngle = outerWinWheelInstance.rotationAngle;
  innerWinWheelInstance.draw(false);
}

// Called when the animation has finished.
function alertPrize()
{
  // The indicated segments from the 2 wheels.
  let winningInnerSegment = innerWinWheelInstance.getIndicatedSegment();
  let winningOuterSegment = outerWinWheelInstance.getIndicatedSegment();

  // Alert the combination of prizes won.
  alert('You won ' + winningInnerSegment.text + ', ' + winningOuterSegment.text);

  // Set things so power and spin button can be clicked again.
  // wheelSpinning = false;

  // Remove all colours from the power level indicators.
  // document.getElementById('pw1').className = "";
  // document.getElementById('pw2').className = "";
  // document.getElementById('pw3').className = "";
}

const options = {
  ...outerWheelOptions,
  innerWheelOptions
};

// this instances code is required only for (2 part wheel)
let outerWinWheelInstance;
let innerWinWheelInstance;

const winWheelInstances = (outerWheelInstance, innerWheelInstance) => {
  if(outerWheelInstance) {
    outerWinWheelInstance = outerWheelInstance
    // Call draw on the outerWheel then the inner wheel to ensure that both are rendered on the canvas.
  }
  if(innerWheelInstance) {
    innerWinWheelInstance = innerWheelInstance
  }
  if(outerWheelInstance && innerWheelInstance) { // now tackled with drawInnerWheel
    // Update the rotationAngle of the innerWheel to match that of the outer wheel - this is a big part of what
    // links them to appear as one 2-part wheel. Call the draw function passing false so the outer wheel is not wiped.
    innerWheelInstance.rotationAngle = outerWheelInstance.rotationAngle;
    if(innerWheelInstance.draw)
      innerWheelInstance.draw(false);
  }
}

const allOptions = {
  ...options,
  outerRadius: (canvasWidth / 2) - (options.lineWidth || 1)
};

onMounted(() => {
  allOptions.outerRadius = (canvasWidth / 2) - (options.lineWidth || 1);
});

</script>

<style scoped>
/* Define your css here inspect dom to see what ids and classes are available */
</style>

DOCS WINWHEEL

Original Author

Douglas McKechie https://github.com/zarocknz

Maintainer

@zohaibtariq https://github.com/zohaibtariq

Please note

This wasn't created by me; It is just a fork of Winwheel.js that supports javascript esm (Ecma Script Modules)