├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── babel.config.js
├── dist
├── vue3-roulette.esm.js
└── vue3-roulette.min.js
├── docs
├── 404.html
├── CNAME
├── assets
│ ├── favicon.adef23e4.ico
│ ├── index.019fedf3.css
│ ├── index.58c620c4.js
│ └── vendor.aed0c264.js
├── index.html
├── og-image.jpg
└── roulette.png
├── favicon.ico
├── index.html
├── jest.config.js
├── package-lock.json
├── package.json
├── pages
├── App.vue
├── components
│ ├── Example.vue
│ ├── Footer.vue
│ ├── ItemsManager.vue
│ ├── Navbar.vue
│ └── WheelManager.vue
├── data
│ ├── examplesData.js
│ └── homeData.js
├── main.js
├── router.js
└── views
│ ├── Custom.vue
│ ├── Docs.vue
│ ├── Examples.vue
│ └── Home.vue
├── postcss.config.js
├── public
├── 404.html
├── CNAME
├── og-image.jpg
└── roulette.png
├── rollup.config.js
├── src
├── components
│ └── Roulette.vue
├── index.js
└── styles
│ └── index.scss
├── tests
└── index.test.js
└── vite.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | indent_style = space
3 | indent_size = 2
4 | end_of_line = lf
5 | trim_trailing_whitespace = true
6 | insert_final_newline = true
7 | max_line_length = 100
8 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /dist
3 | /node_modules
4 | /docs
5 | /tests
6 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 |
4 | extends: ['eslint:recommended', 'plugin:vue/recommended'],
5 |
6 | env: {
7 | node: true,
8 | },
9 |
10 | parserOptions: {
11 | parser: 'babel-eslint',
12 | },
13 |
14 | overrides: [
15 | {
16 | files: 'tests/**/*',
17 | rules: {
18 | // Disable no-undefined rule for Jest tests because `describe()`, `test()`
19 | // are methods available under the global NodeJS namespace.
20 | 'no-undef': 'off',
21 | },
22 | },
23 | ],
24 |
25 | rules: {
26 | // allow paren-less arrow functions
27 | 'arrow-parens': 0,
28 | // allow async-await
29 | 'generator-star-spacing': 0,
30 | // allow debugger during development
31 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
32 |
33 | 'vue/no-v-html': 'off',
34 | },
35 | };
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | coverage
4 | dist
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw?
23 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .editorconfig
2 | .eslintignore
3 | .eslintrc.js
4 | .gitlab-ci.yml
5 | babel.config.js
6 | coverage
7 | jest.config.js
8 | postcss.config.js
9 | sample
10 | src
11 | tests
12 | rollup.config.js
13 | vue.config.js
14 | sample
15 | index.html
16 | vite.config.js
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Ludovic Nitoumbi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vue3 Roulette
2 |
3 | > A customizable and flexible fortune wheel for vue3
4 |
5 | ## Demo
6 |
7 | https://roulette.nitocode.com/
8 |
9 | See also: [codesandbox template](https://codesandbox.io/s/vue3-roulette-c8tml)
10 |
11 | ## Installation
12 |
13 | ### Using npm
14 |
15 | `npm i --save vue3-roulette`
16 |
17 | ## Usage
18 |
19 | #### main.js
20 | ```js
21 | import { createApp } from 'vue'
22 | import App from './App.vue'
23 | import { Roulette } from 'vue3-roulette'
24 |
25 | createApp(App).component("roulette", Roulette).mount('#app')
26 | ```
27 |
28 | #### vuejs html template
29 | ```html
30 |
31 | ```
32 |
33 | #### vuejs script
34 |
35 | Using the [sfc syntax](https://v3.vuejs.org/api/sfc-script-setup.html)
36 | ```html
37 |
51 | ```
52 |
53 | ## Events API
54 |
55 | `wheel-start` and `wheel-end` which provide the item selected
56 |
57 | ```html
58 |
63 | ```
64 |
65 | ## Methods API
66 |
67 | Composition API
68 | ```javascript
69 | wheel.value.launchWheel();
70 | wheel.value.reset();
71 | ```
72 | Option API
73 | ```javascript
74 | this.$refs.wheel.launchWheel();
75 | this.$refs.wheel.reset();
76 | ```
77 |
78 | ## Props API (Wheel)
79 |
80 | | Props | Type | Required | Default | Options | Details |
81 | |------------|------------|----------|------------|----------------|--|
82 | | items | Object | yes | - | | 4 items minimum |
83 | | first-item-index | Object | no | { value: 0 } | |
84 | | wheel-result-index | Object | no | { value: null } | from 0 to items length |
85 | | centered-indicator | Boolean | no | false | |
86 | | indicator-position | String | no | "top" | "top" \| "right" \| "bottom" \| "left" |
87 | | size | Number | no | 300 | | size unit: pixel |
88 | | display-shadow | Boolean | no | false | |
89 | | duration | Number | no | 4 | | duration unit: seconds | |
90 | | result-variation | Number | no | 0 | number between 0 and 100 | varies the result angle to fake wheel smoothness |
91 | | easing | String | no | "ease" | "ease" \| "bounce" | wheel animation |
92 | | counter-clockwise | Boolean | no | false | | rotation direction
93 | | horizontal-content | Boolean | no | false | | text item orientation
94 | | display-border | Boolean | no | false | |
95 | | display-indicator | Boolean | no | false | |
96 |
97 | ## Props API (Wheel base)
98 |
99 | | Props | Type | Required | Default | Options | Details |
100 | |------------|------------|----------|------------|----------------|--|
101 | | base-display | Boolean | no | false | | |
102 | | base-size | Number | no | 100 | | size unit: pixel |
103 | | base-display-shadow | Boolean | no | false | | |
104 | | base-display-indicator | Boolean | no | false | | |
105 | | base-background | String | no | "" | rgb(100,0,0) \| red \| #FF0000 | |
106 |
107 |
108 | ## Slots
109 |
110 | You can use your own html for the wheel base
111 |
112 | ```html
113 |
114 |
115 |
116 |
117 |
118 | ```
119 |
120 | ## Contribution
121 |
122 | ### Project setup
123 |
124 | ```bash
125 | npm install
126 | ```
127 |
128 | ### Compiles and hot-reloads for development
129 |
130 | ```bash
131 | npm run serve
132 | ```
133 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | // Build presets are used when building the component library with Rollup
2 | const buildPresets = [
3 | [
4 | '@babel/preset-env',
5 | {}
6 | ]
7 | ]
8 |
9 | // Dev presets are used when running a local development server
10 | const devPresets = ['@vue/cli-plugin-babel/preset']
11 |
12 | // Test presets are used when running unit-tests
13 | const testPresets = [
14 | [
15 | 'env',
16 | {
17 | targets: {
18 | node: 'current'
19 | }
20 | }
21 | ]
22 | ]
23 |
24 | module.exports = {
25 | presets: process.env.NODE_ENV === 'production'
26 | ? buildPresets : process.env.NODE_ENV === 'development'
27 | ? devPresets : testPresets
28 | }
29 |
--------------------------------------------------------------------------------
/dist/vue3-roulette.esm.js:
--------------------------------------------------------------------------------
1 | import { defineComponent, openBlock, createElementBlock, normalizeClass, normalizeStyle, createElementVNode, renderSlot, createCommentVNode, Fragment, renderList } from 'vue';
2 |
3 | var script = defineComponent({
4 | name: "Roulette",
5 | emits: ["wheelStart", "wheelEnd"],
6 | props: {
7 | items: {
8 | type: Object,
9 | required: true,
10 | validator: function validator(value) {
11 | return value.length >= 4;
12 | }
13 | },
14 | firstItemIndex: {
15 | type: Object,
16 | required: false,
17 | "default": function _default() {
18 | return {
19 | value: 0
20 | };
21 | }
22 | },
23 | wheelResultIndex: {
24 | type: Object,
25 | required: false,
26 | "default": function _default() {
27 | return {
28 | value: null
29 | };
30 | },
31 | validator: function validator(obj) {
32 | return typeof obj.value === "number";
33 | }
34 | },
35 | centeredIndicator: {
36 | type: Boolean,
37 | required: false,
38 | "default": false
39 | },
40 | indicatorPosition: {
41 | type: String,
42 | required: false,
43 | "default": "top",
44 | validator: function validator(value) {
45 | return ["top", "right", "bottom", "left"].includes(value);
46 | }
47 | },
48 | size: {
49 | type: Number,
50 | required: false,
51 | "default": 300
52 | },
53 | displayShadow: {
54 | type: Boolean,
55 | required: false,
56 | "default": false
57 | },
58 | duration: {
59 | type: Number,
60 | required: false,
61 | "default": 4
62 | },
63 | resultVariation: {
64 | type: Number,
65 | required: false,
66 | "default": 0,
67 | validator: function validator(value) {
68 | return value >= 0 && value <= 100;
69 | }
70 | },
71 | easing: {
72 | type: String,
73 | required: false,
74 | "default": "ease",
75 | validator: function validator(value) {
76 | return ["ease", "bounce"].includes(value);
77 | }
78 | },
79 | counterClockwise: {
80 | type: Boolean,
81 | required: false,
82 | "default": false
83 | },
84 | horizontalContent: {
85 | type: Boolean,
86 | required: false,
87 | "default": false
88 | },
89 | displayBorder: {
90 | type: Boolean,
91 | required: false,
92 | "default": false
93 | },
94 | displayIndicator: {
95 | type: Boolean,
96 | required: false,
97 | "default": true
98 | },
99 | baseDisplay: {
100 | type: Boolean,
101 | required: false,
102 | "default": false
103 | },
104 | baseSize: {
105 | type: Number,
106 | required: false,
107 | "default": 100
108 | },
109 | baseDisplayShadow: {
110 | type: Boolean,
111 | required: false,
112 | "default": false
113 | },
114 | baseDisplayIndicator: {
115 | type: Boolean,
116 | required: false,
117 | "default": false
118 | },
119 | baseBackground: {
120 | type: String,
121 | required: false,
122 | "default": ""
123 | }
124 | },
125 | data: function data() {
126 | return {
127 | randomIdRoulette: 0,
128 | itemSelected: null,
129 | processingLock: false
130 | };
131 | },
132 | computed: {
133 | itemAngle: function itemAngle() {
134 | return 360 / this.items.length;
135 | },
136 | startingAngle: function startingAngle() {
137 | if (this.centeredIndicator) {
138 | return -1 * this.firstItemIndex.value * this.itemAngle - this.itemAngle / 2;
139 | } else {
140 | return -1 * this.firstItemIndex.value * this.itemAngle;
141 | }
142 | },
143 | degreesVariation: function degreesVariation() {
144 | if (!this.resultVariation) {
145 | return 0;
146 | }
147 |
148 | var minDegreesVariation = this.itemAngle / 2 * this.resultVariation / 100 * -1;
149 | var maxDegreesVariation = this.itemAngle / 2 * this.resultVariation / 100; // Return random value between min and max degrees variation
150 |
151 | return Number((Math.random() * (maxDegreesVariation - minDegreesVariation) + minDegreesVariation).toFixed(2));
152 | },
153 | counterClockWiseOperator: function counterClockWiseOperator() {
154 | return this.counterClockwise ? -1 : 1;
155 | }
156 | },
157 | mounted: function mounted() {
158 | var _this = this;
159 |
160 | this.randomIdRoulette = Number((Math.random() * (999999 - 1) + 1).toFixed(0));
161 | this.$nextTick(function () {
162 | _this.reset();
163 |
164 | document.querySelector("#wheel-container-".concat(_this.randomIdRoulette, " .wheel")).addEventListener("transitionend", function () {
165 | _this.processingLock = false;
166 |
167 | _this.$emit("wheel-end", _this.itemSelected);
168 | });
169 | });
170 | },
171 | methods: {
172 | reset: function reset() {
173 | this.itemSelected = null;
174 | document.querySelector("#wheel-container-".concat(this.randomIdRoulette, " .wheel")).style.transform = "rotate(".concat(this.startingAngle, "deg)");
175 | },
176 | launchWheel: function launchWheel() {
177 | if (this.processingLock && this.itemSelected != null) {
178 | return;
179 | }
180 |
181 | this.processingLock = true;
182 | var wheelResult;
183 |
184 | if (this.wheelResultIndex.value !== null) {
185 | wheelResult = this.wheelResultIndex.value % this.items.length;
186 | } else {
187 | wheelResult = Math.floor(Math.random() * this.items.length + 1) - 1;
188 | }
189 |
190 | var wheelElt = document.querySelector("#wheel-container-".concat(this.randomIdRoulette, " .wheel"));
191 | this.itemSelected = this.items[wheelResult];
192 | wheelElt.style.transform = "rotate(".concat(this.counterClockWiseOperator * (360 * 3) + -wheelResult * this.itemAngle - this.itemAngle / 2 + this.degreesVariation, "deg)");
193 | this.$emit("wheel-start", this.itemSelected);
194 | }
195 | }
196 | });
197 |
198 | var _hoisted_1 = ["id"];
199 | var _hoisted_2 = {
200 | "class": "wheel-base"
201 | };
202 | var _hoisted_3 = {
203 | key: 0,
204 | "class": "wheel-base-indicator"
205 | };
206 | var _hoisted_4 = ["innerHTML"];
207 | function render(_ctx, _cache, $props, $setup, $data, $options) {
208 | return openBlock(), createElementBlock("div", {
209 | id: "wheel-container-".concat(_ctx.randomIdRoulette),
210 | "class": normalizeClass(["wheel-container", ["indicator-".concat(_ctx.indicatorPosition), {
211 | 'wheel-container-indicator': _ctx.displayIndicator
212 | }, {
213 | 'wheel-container-shadow': _ctx.displayShadow
214 | }, {
215 | 'wheel-container-border': _ctx.displayBorder
216 | }]])
217 | }, [_ctx.baseDisplay ? (openBlock(), createElementBlock("div", {
218 | key: 0,
219 | "class": normalizeClass(["wheel-base-container", [{
220 | 'wheel-base-container-shadow': _ctx.baseDisplayShadow
221 | }]]),
222 | style: normalizeStyle({
223 | width: "".concat(_ctx.baseSize, "px"),
224 | height: "".concat(_ctx.baseSize, "px"),
225 | background: "".concat(_ctx.baseBackground)
226 | })
227 | }, [createElementVNode("div", _hoisted_2, [renderSlot(_ctx.$slots, "baseContent")]), _ctx.baseDisplayIndicator ? (openBlock(), createElementBlock("div", _hoisted_3)) : createCommentVNode("", true)], 6)) : createCommentVNode("", true), createElementVNode("div", {
228 | "class": normalizeClass(["wheel", ["easing-".concat(_ctx.easing), {
229 | 'wheel-border': _ctx.displayBorder
230 | }]]),
231 | style: normalizeStyle({
232 | width: "".concat(_ctx.size, "px"),
233 | height: "".concat(_ctx.size, "px"),
234 | transitionDuration: "".concat(_ctx.duration, "s"),
235 | transform: "rotate(".concat(_ctx.startingAngle, "deg)")
236 | })
237 | }, [(openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.items, function (item, index) {
238 | return openBlock(), createElementBlock("div", {
239 | key: item.id,
240 | "class": "wheel-item",
241 | style: normalizeStyle({
242 | transform: "rotate(".concat(_ctx.itemAngle * index, "deg) skewY(").concat(-(90 - _ctx.itemAngle), "deg)"),
243 | background: item.background
244 | })
245 | }, [createElementVNode("div", {
246 | "class": normalizeClass(["content", {
247 | 'horizontal-content': _ctx.horizontalContent
248 | }]),
249 | style: normalizeStyle({
250 | transform: "skewY(".concat(90 - _ctx.itemAngle, "deg) rotate(").concat(_ctx.itemAngle / 2, "deg)")
251 | })
252 | }, [createElementVNode("span", {
253 | style: normalizeStyle({
254 | color: item.textColor
255 | }),
256 | innerHTML: item.htmlContent
257 | }, null, 12, _hoisted_4)], 6)], 4);
258 | }), 128))], 6)], 10, _hoisted_1);
259 | }
260 |
261 | function styleInject(css, ref) {
262 | if ( ref === void 0 ) ref = {};
263 | var insertAt = ref.insertAt;
264 |
265 | if (!css || typeof document === 'undefined') { return; }
266 |
267 | var head = document.head || document.getElementsByTagName('head')[0];
268 | var style = document.createElement('style');
269 | style.type = 'text/css';
270 |
271 | if (insertAt === 'top') {
272 | if (head.firstChild) {
273 | head.insertBefore(style, head.firstChild);
274 | } else {
275 | head.appendChild(style);
276 | }
277 | } else {
278 | head.appendChild(style);
279 | }
280 |
281 | if (style.styleSheet) {
282 | style.styleSheet.cssText = css;
283 | } else {
284 | style.appendChild(document.createTextNode(css));
285 | }
286 | }
287 |
288 | var css_248z = ".wheel-container[data-v-2d0cf945],\n.wheel-base[data-v-2d0cf945],\n.wheel-base-container[data-v-2d0cf945],\n.wheel-base-indicator[data-v-2d0cf945] {\n transition: transform 1s ease-in-out;\n}\n.wheel-container[data-v-2d0cf945] {\n position: relative;\n display: inline-block;\n overflow: hidden;\n border-radius: 50%;\n cursor: pointer;\n}\n.wheel-container-indicator[data-v-2d0cf945]:before {\n content: \"\";\n position: absolute;\n z-index: 4;\n width: 0;\n height: 0;\n border-left: 20px solid transparent;\n border-right: 20px solid transparent;\n border-top: 20px solid black;\n transform: translateX(-50%);\n}\n.wheel-container.indicator-top[data-v-2d0cf945] {\n transform: rotate(0deg);\n}\n.wheel-container.indicator-right[data-v-2d0cf945] {\n transform: rotate(90deg);\n}\n.wheel-container.indicator-right .wheel-base[data-v-2d0cf945] {\n transform: rotate(-90deg);\n}\n.wheel-container.indicator-bottom[data-v-2d0cf945] {\n transform: rotate(180deg);\n}\n.wheel-container.indicator-bottom .wheel-base[data-v-2d0cf945] {\n transform: rotate(-180deg);\n}\n.wheel-container.indicator-left[data-v-2d0cf945] {\n transform: rotate(270deg);\n}\n.wheel-container.indicator-left .wheel-base[data-v-2d0cf945] {\n transform: rotate(-270deg);\n}\n.wheel-container-border[data-v-2d0cf945] {\n border: 8px solid black;\n}\n.wheel-container-shadow[data-v-2d0cf945] {\n box-shadow: 5px 5px 15px -5px #000000;\n}\n.wheel-base-container[data-v-2d0cf945] {\n position: absolute;\n z-index: 2;\n top: 50%;\n left: 50%;\n border-radius: 50%;\n border: 5px solid black;\n transform: translate(-50%, -50%);\n}\n.wheel-base-container-shadow[data-v-2d0cf945] {\n box-shadow: 5px 5px 15px -5px #000000;\n}\n.wheel-base-container .wheel-base[data-v-2d0cf945] {\n position: absolute;\n z-index: 2;\n display: flex;\n justify-content: center;\n align-items: center;\n overflow: hidden;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n}\n.wheel-base-container .wheel-base-indicator[data-v-2d0cf945] {\n position: absolute;\n z-index: 1;\n width: 100%;\n height: 100%;\n}\n.wheel-base-container .wheel-base-indicator[data-v-2d0cf945]:before {\n content: \"\";\n position: absolute;\n z-index: 1;\n top: -20px;\n width: 0;\n height: 0;\n border-left: 20px solid transparent;\n border-right: 20px solid transparent;\n border-bottom: 20px solid black;\n transform: translateX(-50%);\n}\n.wheel[data-v-2d0cf945] {\n background: white;\n border-radius: 50%;\n margin: auto;\n overflow: hidden;\n}\n.wheel.easing-ease[data-v-2d0cf945] {\n transition: transform cubic-bezier(0.65, 0, 0.35, 1);\n}\n.wheel.easing-bounce[data-v-2d0cf945] {\n transition: transform cubic-bezier(0.49, 0.02, 0.52, 1.12);\n}\n.wheel-border[data-v-2d0cf945]:after {\n content: \"\";\n width: 100%;\n height: 100%;\n position: absolute;\n left: 0;\n top: 0;\n z-index: 3;\n border-radius: 50%;\n background-image: linear-gradient(to left, black 33%, rgba(255, 255, 255, 0) 0%);\n background-position: bottom;\n background-size: 3px 1px;\n /* background:linear-gradient(red,purple,orange); */\n -webkit-mask: radial-gradient(transparent 65%, #000 66%);\n mask: radial-gradient(transparent 65%, #000 66%);\n}\n.wheel-item[data-v-2d0cf945] {\n overflow: hidden;\n position: absolute;\n top: 0;\n right: 0;\n width: 50%;\n height: 50%;\n transform-origin: 0% 100%;\n border: 1px solid black;\n}\n.wheel-item[data-v-2d0cf945]:nth-child(odd) {\n background-color: skyblue;\n}\n.wheel-item[data-v-2d0cf945]:nth-child(even) {\n background-color: pink;\n}\n.wheel .content[data-v-2d0cf945] {\n position: absolute;\n left: -100%;\n width: 200%;\n height: 200%;\n text-align: center;\n transform: skewY(30deg) rotate(0deg);\n padding-top: 20px;\n}\n.wheel .content.horizontal-content[data-v-2d0cf945] {\n left: initial;\n right: 100%;\n width: 50%;\n height: 250%;\n text-align: right;\n}\n.wheel .content.horizontal-content span[data-v-2d0cf945] {\n display: block;\n transform: rotate(270deg);\n}";
289 | styleInject(css_248z);
290 |
291 | script.render = render;
292 | script.__scopeId = "data-v-2d0cf945";
293 |
294 | export { script as Roulette };
295 |
--------------------------------------------------------------------------------
/dist/vue3-roulette.min.js:
--------------------------------------------------------------------------------
1 | var vue3Roulette=function(e,t){"use strict";var n=t.defineComponent({name:"Roulette",emits:["wheelStart","wheelEnd"],props:{items:{type:Object,required:!0,validator:function(e){return e.length>=4}},firstItemIndex:{type:Object,required:!1,default:function(){return{value:0}}},wheelResultIndex:{type:Object,required:!1,default:function(){return{value:null}},validator:function(e){return"number"==typeof e.value}},centeredIndicator:{type:Boolean,required:!1,default:!1},indicatorPosition:{type:String,required:!1,default:"top",validator:function(e){return["top","right","bottom","left"].includes(e)}},size:{type:Number,required:!1,default:300},displayShadow:{type:Boolean,required:!1,default:!1},duration:{type:Number,required:!1,default:4},resultVariation:{type:Number,required:!1,default:0,validator:function(e){return e>=0&&e<=100}},easing:{type:String,required:!1,default:"ease",validator:function(e){return["ease","bounce"].includes(e)}},counterClockwise:{type:Boolean,required:!1,default:!1},horizontalContent:{type:Boolean,required:!1,default:!1},displayBorder:{type:Boolean,required:!1,default:!1},displayIndicator:{type:Boolean,required:!1,default:!0},baseDisplay:{type:Boolean,required:!1,default:!1},baseSize:{type:Number,required:!1,default:100},baseDisplayShadow:{type:Boolean,required:!1,default:!1},baseDisplayIndicator:{type:Boolean,required:!1,default:!1},baseBackground:{type:String,required:!1,default:""}},data:function(){return{randomIdRoulette:0,itemSelected:null,processingLock:!1}},computed:{itemAngle:function(){return 360/this.items.length},startingAngle:function(){return this.centeredIndicator?-1*this.firstItemIndex.value*this.itemAngle-this.itemAngle/2:-1*this.firstItemIndex.value*this.itemAngle},degreesVariation:function(){if(!this.resultVariation)return 0;var e=this.itemAngle/2*this.resultVariation/100*-1,t=this.itemAngle/2*this.resultVariation/100;return Number((Math.random()*(t-e)+e).toFixed(2))},counterClockWiseOperator:function(){return this.counterClockwise?-1:1}},mounted:function(){var e=this;this.randomIdRoulette=Number((999998*Math.random()+1).toFixed(0)),this.$nextTick((function(){e.reset(),document.querySelector("#wheel-container-".concat(e.randomIdRoulette," .wheel")).addEventListener("transitionend",(function(){e.processingLock=!1,e.$emit("wheel-end",e.itemSelected)}))}))},methods:{reset:function(){this.itemSelected=null,document.querySelector("#wheel-container-".concat(this.randomIdRoulette," .wheel")).style.transform="rotate(".concat(this.startingAngle,"deg)")},launchWheel:function(){if(!this.processingLock||null==this.itemSelected){var e;this.processingLock=!0,e=null!==this.wheelResultIndex.value?this.wheelResultIndex.value%this.items.length:Math.floor(Math.random()*this.items.length+1)-1;var t=document.querySelector("#wheel-container-".concat(this.randomIdRoulette," .wheel"));this.itemSelected=this.items[e],t.style.transform="rotate(".concat(1080*this.counterClockWiseOperator+-e*this.itemAngle-this.itemAngle/2+this.degreesVariation,"deg)"),this.$emit("wheel-start",this.itemSelected)}}}}),a=["id"],o={class:"wheel-base"},r={key:0,class:"wheel-base-indicator"},i=["innerHTML"];return function(e,t){void 0===t&&(t={});var n=t.insertAt;if(e&&"undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css","top"===n&&a.firstChild?a.insertBefore(o,a.firstChild):a.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e))}}('.wheel-container[data-v-2d0cf945],\n.wheel-base[data-v-2d0cf945],\n.wheel-base-container[data-v-2d0cf945],\n.wheel-base-indicator[data-v-2d0cf945] {\n transition: transform 1s ease-in-out;\n}\n.wheel-container[data-v-2d0cf945] {\n position: relative;\n display: inline-block;\n overflow: hidden;\n border-radius: 50%;\n cursor: pointer;\n}\n.wheel-container-indicator[data-v-2d0cf945]:before {\n content: "";\n position: absolute;\n z-index: 4;\n width: 0;\n height: 0;\n border-left: 20px solid transparent;\n border-right: 20px solid transparent;\n border-top: 20px solid black;\n transform: translateX(-50%);\n}\n.wheel-container.indicator-top[data-v-2d0cf945] {\n transform: rotate(0deg);\n}\n.wheel-container.indicator-right[data-v-2d0cf945] {\n transform: rotate(90deg);\n}\n.wheel-container.indicator-right .wheel-base[data-v-2d0cf945] {\n transform: rotate(-90deg);\n}\n.wheel-container.indicator-bottom[data-v-2d0cf945] {\n transform: rotate(180deg);\n}\n.wheel-container.indicator-bottom .wheel-base[data-v-2d0cf945] {\n transform: rotate(-180deg);\n}\n.wheel-container.indicator-left[data-v-2d0cf945] {\n transform: rotate(270deg);\n}\n.wheel-container.indicator-left .wheel-base[data-v-2d0cf945] {\n transform: rotate(-270deg);\n}\n.wheel-container-border[data-v-2d0cf945] {\n border: 8px solid black;\n}\n.wheel-container-shadow[data-v-2d0cf945] {\n box-shadow: 5px 5px 15px -5px #000000;\n}\n.wheel-base-container[data-v-2d0cf945] {\n position: absolute;\n z-index: 2;\n top: 50%;\n left: 50%;\n border-radius: 50%;\n border: 5px solid black;\n transform: translate(-50%, -50%);\n}\n.wheel-base-container-shadow[data-v-2d0cf945] {\n box-shadow: 5px 5px 15px -5px #000000;\n}\n.wheel-base-container .wheel-base[data-v-2d0cf945] {\n position: absolute;\n z-index: 2;\n display: flex;\n justify-content: center;\n align-items: center;\n overflow: hidden;\n width: 100%;\n height: 100%;\n border-radius: 50%;\n}\n.wheel-base-container .wheel-base-indicator[data-v-2d0cf945] {\n position: absolute;\n z-index: 1;\n width: 100%;\n height: 100%;\n}\n.wheel-base-container .wheel-base-indicator[data-v-2d0cf945]:before {\n content: "";\n position: absolute;\n z-index: 1;\n top: -20px;\n width: 0;\n height: 0;\n border-left: 20px solid transparent;\n border-right: 20px solid transparent;\n border-bottom: 20px solid black;\n transform: translateX(-50%);\n}\n.wheel[data-v-2d0cf945] {\n background: white;\n border-radius: 50%;\n margin: auto;\n overflow: hidden;\n}\n.wheel.easing-ease[data-v-2d0cf945] {\n transition: transform cubic-bezier(0.65, 0, 0.35, 1);\n}\n.wheel.easing-bounce[data-v-2d0cf945] {\n transition: transform cubic-bezier(0.49, 0.02, 0.52, 1.12);\n}\n.wheel-border[data-v-2d0cf945]:after {\n content: "";\n width: 100%;\n height: 100%;\n position: absolute;\n left: 0;\n top: 0;\n z-index: 3;\n border-radius: 50%;\n background-image: linear-gradient(to left, black 33%, rgba(255, 255, 255, 0) 0%);\n background-position: bottom;\n background-size: 3px 1px;\n /* background:linear-gradient(red,purple,orange); */\n -webkit-mask: radial-gradient(transparent 65%, #000 66%);\n mask: radial-gradient(transparent 65%, #000 66%);\n}\n.wheel-item[data-v-2d0cf945] {\n overflow: hidden;\n position: absolute;\n top: 0;\n right: 0;\n width: 50%;\n height: 50%;\n transform-origin: 0% 100%;\n border: 1px solid black;\n}\n.wheel-item[data-v-2d0cf945]:nth-child(odd) {\n background-color: skyblue;\n}\n.wheel-item[data-v-2d0cf945]:nth-child(even) {\n background-color: pink;\n}\n.wheel .content[data-v-2d0cf945] {\n position: absolute;\n left: -100%;\n width: 200%;\n height: 200%;\n text-align: center;\n transform: skewY(30deg) rotate(0deg);\n padding-top: 20px;\n}\n.wheel .content.horizontal-content[data-v-2d0cf945] {\n left: initial;\n right: 100%;\n width: 50%;\n height: 250%;\n text-align: right;\n}\n.wheel .content.horizontal-content span[data-v-2d0cf945] {\n display: block;\n transform: rotate(270deg);\n}'),n.render=function(e,n,d,l,s,c){return t.openBlock(),t.createElementBlock("div",{id:"wheel-container-".concat(e.randomIdRoulette),class:t.normalizeClass(["wheel-container",["indicator-".concat(e.indicatorPosition),{"wheel-container-indicator":e.displayIndicator},{"wheel-container-shadow":e.displayShadow},{"wheel-container-border":e.displayBorder}]])},[e.baseDisplay?(t.openBlock(),t.createElementBlock("div",{key:0,class:t.normalizeClass(["wheel-base-container",[{"wheel-base-container-shadow":e.baseDisplayShadow}]]),style:t.normalizeStyle({width:"".concat(e.baseSize,"px"),height:"".concat(e.baseSize,"px"),background:"".concat(e.baseBackground)})},[t.createElementVNode("div",o,[t.renderSlot(e.$slots,"baseContent")]),e.baseDisplayIndicator?(t.openBlock(),t.createElementBlock("div",r)):t.createCommentVNode("",!0)],6)):t.createCommentVNode("",!0),t.createElementVNode("div",{class:t.normalizeClass(["wheel",["easing-".concat(e.easing),{"wheel-border":e.displayBorder}]]),style:t.normalizeStyle({width:"".concat(e.size,"px"),height:"".concat(e.size,"px"),transitionDuration:"".concat(e.duration,"s"),transform:"rotate(".concat(e.startingAngle,"deg)")})},[(t.openBlock(!0),t.createElementBlock(t.Fragment,null,t.renderList(e.items,(function(n,a){return t.openBlock(),t.createElementBlock("div",{key:n.id,class:"wheel-item",style:t.normalizeStyle({transform:"rotate(".concat(e.itemAngle*a,"deg) skewY(").concat(-(90-e.itemAngle),"deg)"),background:n.background})},[t.createElementVNode("div",{class:t.normalizeClass(["content",{"horizontal-content":e.horizontalContent}]),style:t.normalizeStyle({transform:"skewY(".concat(90-e.itemAngle,"deg) rotate(").concat(e.itemAngle/2,"deg)")})},[t.createElementVNode("span",{style:t.normalizeStyle({color:n.textColor}),innerHTML:n.htmlContent},null,12,i)],6)],4)})),128))],6)],10,a)},n.__scopeId="data-v-2d0cf945",e.Roulette=n,Object.defineProperty(e,"__esModule",{value:!0}),e}({},Vue);
--------------------------------------------------------------------------------
/docs/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Vue3 Roulette
13 |
22 |
23 |
24 |
39 |
40 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | roulette.nitocode.com
--------------------------------------------------------------------------------
/docs/assets/favicon.adef23e4.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nitocode/vue3-roulette/c4c869f98f9e0e1ef614685b6cf2d47a1bd58294/docs/assets/favicon.adef23e4.ico
--------------------------------------------------------------------------------
/docs/assets/index.019fedf3.css:
--------------------------------------------------------------------------------
1 | @import"https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.0.0/github-markdown-light.min.css";.item{display:flex;flex-direction:row;justify-content:center;align-items:center}.wheel-container[data-v-313a8203],.wheel-base[data-v-313a8203],.wheel-base-container[data-v-313a8203],.wheel-base-indicator[data-v-313a8203]{transition:transform 1s ease-in-out}.wheel-container[data-v-313a8203]{position:relative;display:inline-block;overflow:hidden;border-radius:50%;cursor:pointer}.wheel-container-indicator[data-v-313a8203]:before{content:"";position:absolute;z-index:4;width:0;height:0;border-left:20px solid transparent;border-right:20px solid transparent;border-top:20px solid black;transform:translate(-50%)}.wheel-container.indicator-top[data-v-313a8203]{transform:rotate(0)}.wheel-container.indicator-right[data-v-313a8203]{transform:rotate(90deg)}.wheel-container.indicator-right .wheel-base[data-v-313a8203]{transform:rotate(-90deg)}.wheel-container.indicator-bottom[data-v-313a8203]{transform:rotate(180deg)}.wheel-container.indicator-bottom .wheel-base[data-v-313a8203]{transform:rotate(-180deg)}.wheel-container.indicator-left[data-v-313a8203]{transform:rotate(270deg)}.wheel-container.indicator-left .wheel-base[data-v-313a8203]{transform:rotate(-270deg)}.wheel-container-border[data-v-313a8203]{border:8px solid black}.wheel-container-shadow[data-v-313a8203]{box-shadow:5px 5px 15px -5px #000}.wheel-base-container[data-v-313a8203]{position:absolute;z-index:2;top:50%;left:50%;border-radius:50%;border:5px solid black;transform:translate(-50%,-50%)}.wheel-base-container-shadow[data-v-313a8203]{box-shadow:5px 5px 15px -5px #000}.wheel-base-container .wheel-base[data-v-313a8203]{position:absolute;z-index:2;display:flex;justify-content:center;align-items:center;overflow:hidden;width:100%;height:100%;border-radius:50%}.wheel-base-container .wheel-base-indicator[data-v-313a8203]{position:absolute;z-index:1;width:100%;height:100%}.wheel-base-container .wheel-base-indicator[data-v-313a8203]:before{content:"";position:absolute;z-index:1;top:-20px;width:0;height:0;border-left:20px solid transparent;border-right:20px solid transparent;border-bottom:20px solid black;transform:translate(-50%)}.wheel[data-v-313a8203]{background:white;border-radius:50%;margin:auto;overflow:hidden}.wheel.easing-ease[data-v-313a8203]{transition:transform cubic-bezier(.65,0,.35,1)}.wheel.easing-bounce[data-v-313a8203]{transition:transform cubic-bezier(.49,.02,.52,1.12)}.wheel-border[data-v-313a8203]:after{content:"";width:100%;height:100%;position:absolute;left:0;top:0;z-index:3;border-radius:50%;background-image:linear-gradient(to left,black 33%,rgba(255,255,255,0) 0%);background-position:bottom;background-size:3px 1px;-webkit-mask:radial-gradient(transparent 65%,#000 66%);mask:radial-gradient(transparent 65%,#000 66%)}.wheel-item[data-v-313a8203]{overflow:hidden;position:absolute;top:0;right:0;width:50%;height:50%;transform-origin:0% 100%;border:1px solid black}.wheel-item[data-v-313a8203]:nth-child(odd){background-color:#87ceeb}.wheel-item[data-v-313a8203]:nth-child(even){background-color:pink}.wheel .content[data-v-313a8203]{position:absolute;left:-100%;width:200%;height:200%;text-align:center;transform:skewY(30deg) rotate(0);padding-top:20px}.wheel .content.horizontal-content[data-v-313a8203]{left:initial;right:100%;width:50%;height:250%;text-align:right}.wheel .content.horizontal-content span[data-v-313a8203]{display:block;transform:rotate(270deg)}.wheel-anim{transition:transform 4s cubic-bezier(.58,-.26,.24,1.11);transform:rotate(-1800deg) scale(1.25)}.wheel-anim-started{transform:rotate(0) scale(1)}[data-v-65fe6d42] img{display:none}@media screen and (max-width: 450px){[data-v-2a21abdc] .big-wheel .relative{transform:scale(.7)}}.drawer-toggle:checked~.drawer-content{z-index:100}.drawer-toggle:checked~.drawer-side{z-index:101}.fade-enter-active,.fade-leave-active{transition:opacity .5s ease}.fade-enter-from,.fade-leave-to{opacity:0}
2 |
--------------------------------------------------------------------------------
/docs/assets/index.58c620c4.js:
--------------------------------------------------------------------------------
1 | var L=Object.defineProperty,T=Object.defineProperties;var Y=Object.getOwnPropertyDescriptors;var M=Object.getOwnPropertySymbols;var J=Object.prototype.hasOwnProperty,K=Object.prototype.propertyIsEnumerable;var U=(t,o,s)=>o in t?L(t,o,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[o]=s,S=(t,o)=>{for(var s in o||(o={}))J.call(o,s)&&U(t,s,o[s]);if(M)for(var s of M(o))K.call(o,s)&&U(t,s,o[s]);return t},B=(t,o)=>T(t,Y(o));import{o as r,c as h,a as e,b as g,F as I,r as A,w as m,v as G,d as C,e as y,f as q,n as f,g as Z,h as k,i as Q,j as b,k as v,l as w,m as $,p as D,q as N,s as p,t as X,u as ee,x as W,y as te,z as ne,T as oe,A as se,B as ae}from"./vendor.aed0c264.js";const ie=function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))l(n);new MutationObserver(n=>{for(const i of n)if(i.type==="childList")for(const a of i.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&l(a)}).observe(document,{childList:!0,subtree:!0});function s(n){const i={};return n.integrity&&(i.integrity=n.integrity),n.referrerpolicy&&(i.referrerPolicy=n.referrerpolicy),n.crossorigin==="use-credentials"?i.credentials="include":n.crossorigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function l(n){if(n.ep)return;n.ep=!0;const i=s(n);fetch(n.href,i)}};ie();var _=(t,o)=>{for(const[s,l]of o)t[s]=l;return t};const le={name:"ItemsManager",emits:["updateItems"],props:{initialFirstItemIndex:{type:Object,required:!1,default:null},initialItems:{type:Object,required:!0}},data(){return{firstItemIndex:null,items:[]}},mounted(){this.items=this.initialItems,this.firstItemIndex=this.initialFirstItemIndex},methods:{add(){const t=this.items[this.items.length-1];this.items.push({id:t.id+1,name:"",htmlContent:"",textColor:"",background:""})},remove(t){this.items.length<5||this.items.splice(t,1)},removeAll(){this.items=[]},update(){this.$emit("update-items",this.items)}}},re={class:"table w-full"},de={class:"text-center"},ce={key:0},ue=e("th",null,"Name",-1),he=e("th",null,"Html content",-1),me=e("th",null,"Text color",-1),ge=e("th",null,"Background",-1),pe=e("th",null,"Delete",-1),be={key:0},fe=["value"],we=["onUpdate:modelValue"],Ce=["onUpdate:modelValue"],_e=["onUpdate:modelValue"],ye=["onUpdate:modelValue"],ve={class:"text-center"},ke=["onClick"],xe=e("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",class:"inline-block w-4 h-4 stroke-current"},[e("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M6 18L18 6M6 6l12 12"})],-1),Se=[xe],Ie={class:"text-center"},De={key:0},ze=e("th",null,"Name",-1),Ee=e("th",null,"Html content",-1),Be=e("th",null,"Text color",-1),Ae=e("th",null,"Background",-1),$e=e("th",null,"Delete",-1),Ne=e("div",{class:"divider"},null,-1);function Ve(t,o,s,l,n,i){return r(),h("div",null,[e("table",re,[e("thead",de,[e("tr",null,[s.initialFirstItemIndex!=null?(r(),h("th",ce," First Item ")):g("",!0),ue,he,me,ge,pe])]),e("tbody",null,[(r(!0),h(I,null,A(n.items,(a,d)=>(r(),h("tr",{key:a.id},[s.initialFirstItemIndex!=null?(r(),h("td",be,[m(e("input",{"onUpdate:modelValue":o[0]||(o[0]=u=>n.firstItemIndex.value=u),name:"firstItem",class:"block mx-auto radio radio-primary",type:"radio",value:d},null,8,fe),[[G,n.firstItemIndex.value]])])):g("",!0),e("td",null,[m(e("input",{"onUpdate:modelValue":u=>a.name=u,class:"input input-bordered w-full",type:"text"},null,8,we),[[C,a.name]])]),e("td",null,[m(e("textarea",{"onUpdate:modelValue":u=>a.htmlContent=u,class:"textarea h-12 textarea-bordered textarea-primary w-full"},null,8,Ce),[[C,a.htmlContent]])]),e("td",null,[m(e("input",{"onUpdate:modelValue":u=>a.textColor=u,class:"input input-bordered w-full",type:"text"},null,8,_e),[[C,a.textColor]])]),e("td",null,[m(e("input",{"onUpdate:modelValue":u=>a.background=u,class:"input input-bordered w-full",type:"text"},null,8,ye),[[C,a.background]])]),e("td",ve,[e("button",{class:"btn btn-error btn-outline btn-circle btn-sm",onClick:u=>i.remove(d)},Se,8,ke)])]))),128))]),e("tfoot",Ie,[e("tr",null,[s.initialFirstItemIndex!=null?(r(),h("th",De," First Item ")):g("",!0),ze,Ee,Be,Ae,$e])])]),Ne,e("div",null,[e("button",{class:"btn btn-primary",onClick:o[1]||(o[1]=(...a)=>i.add&&i.add(...a))}," Add Item ")])])}var V=_(le,[["render",Ve]]);const Re={name:"WheelManager",props:{initialSettings:{type:Object,required:!0}},data(){return{settings:{}}},mounted(){this.settings=this.initialSettings}},He={class:"flex flex-col lg:flex-row items-top justify-around"},Me=e("p",{class:"text-xl mb-4"}," Wheel manager ",-1),Ue={class:"form-control"},qe={class:"cursor-pointer label"},We=e("span",{class:"label-text"},"Display Shadow",-1),Pe={class:"form-control"},Fe={class:"cursor-pointer label"},Oe=e("span",{class:"label-text"},"Display Border",-1),je={class:"form-control"},Le={class:"cursor-pointer label"},Te=e("span",{class:"label-text"},"Display Indicator",-1),Ye={class:"form-control"},Je={class:"cursor-pointer label"},Ke=e("span",{class:"label-text"},"Centered indicator",-1),Ge={class:"form-control"},Ze={class:"cursor-pointer label"},Qe=e("span",{class:"label-text"},"Counter clockwise",-1),Xe={class:"form-control"},et={class:"cursor-pointer label"},tt=e("span",{class:"label-text"},"Horizontal content",-1),nt={class:"form-control"},ot=e("label",{class:"label"},[e("span",{class:"label-text"},"Size")],-1),st={class:"input-group input-group-md"},at=e("span",null,"px",-1),it={class:"form-control"},lt=e("label",{class:"label"},[e("span",{class:"label-text"},"Duration")],-1),rt={class:"input-group input-group-md"},dt=e("span",null,"\xA0s\xA0",-1),ct={class:"form-control"},ut=e("label",{class:"label"},[e("span",{class:"label-text"},"Variation")],-1),ht={class:"input-group input-group-md"},mt=e("span",null,"%\xA0",-1),gt={class:"form-control w-full max-w-xs"},pt=e("label",{for:"position",class:"label"},[e("span",{class:"label-text"},"Indicator position")],-1),bt=e("option",{value:"top"}," Top ",-1),ft=e("option",{value:"right"}," Right ",-1),wt=e("option",{value:"bottom"}," Bottom ",-1),Ct=e("option",{value:"left"}," Left ",-1),_t=[bt,ft,wt,Ct],yt={class:"form-control w-full max-w-xs"},vt=e("label",{for:"easing",class:"label"},[e("span",{class:"label-text"},"Easing")],-1),kt=e("option",{value:"ease"}," Ease ",-1),xt=e("option",{value:"bounce"}," Bounce ",-1),St=[kt,xt],It=e("div",{class:"divider lg:divider-vertical"},null,-1),Dt=e("p",{class:"text-xl mb-4"}," Wheel base manager ",-1),zt={class:"form-control"},Et={class:"cursor-pointer label"},Bt=e("span",{class:"label-text"},"Display base",-1),At={class:"form-control"},$t={class:"cursor-pointer label"},Nt=e("span",{class:"label-text"},"Display base shadow",-1),Vt={class:"form-control"},Rt={class:"cursor-pointer label"},Ht=e("span",{class:"label-text"},"Display base indicator",-1),Mt={class:"form-control"},Ut=e("label",{class:"label"},[e("span",{class:"label-text"},"Base size")],-1),qt={class:"input-group input-group-md"},Wt=e("span",null,"px",-1),Pt={class:"form-control"},Ft=e("label",{class:"label"},[e("span",{class:"label-text"},"Base background")],-1),Ot={class:"form-control"},jt=e("label",{class:"label"},[e("span",{class:"label-text"},"Base HTML Content")],-1);function Lt(t,o,s,l,n,i){return r(),h("div",He,[e("div",null,[Me,e("div",Ue,[e("label",qe,[We,m(e("input",{id:"shadow","onUpdate:modelValue":o[0]||(o[0]=a=>n.settings.displayShadow=a),class:"toggle toggle-primary",type:"checkbox"},null,512),[[y,n.settings.displayShadow]])])]),e("div",Pe,[e("label",Fe,[Oe,m(e("input",{id:"border","onUpdate:modelValue":o[1]||(o[1]=a=>n.settings.displayBorder=a),class:"toggle toggle-primary",type:"checkbox"},null,512),[[y,n.settings.displayBorder]])])]),e("div",je,[e("label",Le,[Te,m(e("input",{id:"indicator","onUpdate:modelValue":o[2]||(o[2]=a=>n.settings.displayIndicator=a),class:"toggle toggle-primary",type:"checkbox"},null,512),[[y,n.settings.displayIndicator]])])]),e("div",Ye,[e("label",Je,[Ke,m(e("input",{id:"centered","onUpdate:modelValue":o[3]||(o[3]=a=>n.settings.centeredIndicator=a),type:"checkbox",class:"checkbox checkbox-primary"},null,512),[[y,n.settings.centeredIndicator]])])]),e("div",Ge,[e("label",Ze,[Qe,m(e("input",{id:"clock","onUpdate:modelValue":o[4]||(o[4]=a=>n.settings.counterClockwise=a),type:"checkbox",class:"checkbox checkbox-primary"},null,512),[[y,n.settings.counterClockwise]])])]),e("div",Xe,[e("label",et,[tt,m(e("input",{id:"horizontal","onUpdate:modelValue":o[5]||(o[5]=a=>n.settings.horizontalContent=a),type:"checkbox",class:"checkbox checkbox-primary"},null,512),[[y,n.settings.horizontalContent]])])]),e("div",nt,[ot,e("label",st,[m(e("input",{id:"size","onUpdate:modelValue":o[6]||(o[6]=a=>n.settings.size=a),class:"input input-primary input-bordered input-md w-full",type:"number"},null,512),[[C,n.settings.size]]),at])]),e("div",it,[lt,e("label",rt,[m(e("input",{id:"duration","onUpdate:modelValue":o[7]||(o[7]=a=>n.settings.duration=a),class:"input input-primary input-bordered input-md w-full",type:"number"},null,512),[[C,n.settings.duration]]),dt])]),e("div",ct,[ut,e("label",ht,[m(e("input",{id:"variation","onUpdate:modelValue":o[8]||(o[8]=a=>n.settings.resultVariation=a),class:"input input-primary input-bordered input-md w-full",type:"number"},null,512),[[C,n.settings.resultVariation]]),mt]),m(e("input",{"onUpdate:modelValue":o[9]||(o[9]=a=>n.settings.resultVariation=a),type:"range",max:"100",class:"range range-primary mt-2"},null,512),[[C,n.settings.resultVariation]])]),e("div",gt,[pt,m(e("select",{id:"position","onUpdate:modelValue":o[10]||(o[10]=a=>n.settings.indicatorPosition=a),name:"position",class:"select select-bordered select-primary w-full max-w-xs"},_t,512),[[q,n.settings.indicatorPosition]])]),e("div",yt,[vt,m(e("select",{id:"easing","onUpdate:modelValue":o[11]||(o[11]=a=>n.settings.easing=a),name:"easing",class:"select select-bordered select-primary w-full max-w-xs"},St,512),[[q,n.settings.easing]])])]),It,e("div",null,[Dt,e("div",zt,[e("label",Et,[Bt,m(e("input",{id:"base","onUpdate:modelValue":o[12]||(o[12]=a=>n.settings.baseDisplay=a),class:"toggle toggle-primary",type:"checkbox"},null,512),[[y,n.settings.baseDisplay]])])]),e("div",{class:f({"opacity-50":!n.settings.baseDisplay})},[e("div",At,[e("label",$t,[Nt,m(e("input",{id:"baseShadow","onUpdate:modelValue":o[13]||(o[13]=a=>n.settings.baseDisplayShadow=a),class:"toggle toggle-primary",type:"checkbox"},null,512),[[y,n.settings.baseDisplayShadow]])])]),e("div",Vt,[e("label",Rt,[Ht,m(e("input",{id:"baseIndicator","onUpdate:modelValue":o[14]||(o[14]=a=>n.settings.baseDisplayIndicator=a),class:"toggle toggle-primary",type:"checkbox"},null,512),[[y,n.settings.baseDisplayIndicator]])])]),e("div",Mt,[Ut,e("label",qt,[m(e("input",{id:"baseSize","onUpdate:modelValue":o[15]||(o[15]=a=>n.settings.baseSize=a),class:"input input-primary input-bordered input-md w-full",type:"number"},null,512),[[C,n.settings.baseSize]]),Wt])]),e("div",Pt,[Ft,m(e("input",{id:"baseBackground","onUpdate:modelValue":o[16]||(o[16]=a=>n.settings.baseBackground=a),class:"input input-primary input-bordered",type:"text"},null,512),[[C,n.settings.baseBackground]])]),e("div",Ot,[jt,m(e("textarea",{"onUpdate:modelValue":o[17]||(o[17]=a=>n.settings.baseHtmlContent=a),class:"textarea h-24 textarea-bordered textarea-primary"},null,512),[[C,n.settings.baseHtmlContent]])])],2)])])}var R=_(Re,[["render",Lt]]);const Tt=Z({name:"Roulette",emits:["wheelStart","wheelEnd"],props:{items:{type:Object,required:!0,validator(t){return t.length>=4}},firstItemIndex:{type:Object,required:!1,default(){return{value:0}}},wheelResultIndex:{type:Object,required:!1,default(){return{value:null}},validator(t){return typeof t.value=="number"}},centeredIndicator:{type:Boolean,required:!1,default:!1},indicatorPosition:{type:String,required:!1,default:"top",validator(t){return["top","right","bottom","left"].includes(t)}},size:{type:Number,required:!1,default:300},displayShadow:{type:Boolean,required:!1,default:!1},duration:{type:Number,required:!1,default:4},resultVariation:{type:Number,required:!1,default:0,validator(t){return t>=0&&t<=100}},easing:{type:String,required:!1,default:"ease",validator(t){return["ease","bounce"].includes(t)}},counterClockwise:{type:Boolean,required:!1,default:!1},horizontalContent:{type:Boolean,required:!1,default:!1},displayBorder:{type:Boolean,required:!1,default:!1},displayIndicator:{type:Boolean,required:!1,default:!0},baseDisplay:{type:Boolean,required:!1,default:!1},baseSize:{type:Number,required:!1,default:100},baseDisplayShadow:{type:Boolean,required:!1,default:!1},baseDisplayIndicator:{type:Boolean,required:!1,default:!1},baseBackground:{type:String,required:!1,default:""}},data(){return{randomIdRoulette:0,itemSelected:null,processingLock:!1}},computed:{itemAngle:function(){return 360/this.items.length},startingAngle:function(){return this.centeredIndicator?-1*this.firstItemIndex.value*this.itemAngle-this.itemAngle/2:-1*this.firstItemIndex.value*this.itemAngle},degreesVariation:function(){if(!this.resultVariation)return 0;const t=this.itemAngle/2*this.resultVariation/100*-1,o=this.itemAngle/2*this.resultVariation/100;return Number((Math.random()*(o-t)+t).toFixed(2))},counterClockWiseOperator:function(){return this.counterClockwise?-1:1}},mounted(){this.randomIdRoulette=Number((Math.random()*(999999-1)+1).toFixed(0)),this.$nextTick(()=>{this.reset(),document.querySelector(`#wheel-container-${this.randomIdRoulette} .wheel`).addEventListener("transitionend",()=>{this.processingLock=!1,this.$emit("wheel-end",this.itemSelected)})})},methods:{reset(){this.itemSelected=null,document.querySelector(`#wheel-container-${this.randomIdRoulette} .wheel`).style.transform=`rotate(${this.startingAngle}deg)`},launchWheel(){if(this.processingLock&&this.itemSelected!=null)return;this.processingLock=!0;let t;this.wheelResultIndex.value!==null?t=this.wheelResultIndex.value%this.items.length:t=Math.floor(Math.random()*this.items.length+1)-1;const o=document.querySelector(`#wheel-container-${this.randomIdRoulette} .wheel`);this.itemSelected=this.items[t],o.style.transform=`rotate(${this.counterClockWiseOperator*(360*3)+-t*this.itemAngle-this.itemAngle/2+this.degreesVariation}deg)`,this.$emit("wheel-start",this.itemSelected)}}}),Yt=["id"],Jt={class:"wheel-base"},Kt={key:0,class:"wheel-base-indicator"},Gt=["innerHTML"];function Zt(t,o,s,l,n,i){return r(),h("div",{id:`wheel-container-${t.randomIdRoulette}`,class:f(["wheel-container",[`indicator-${t.indicatorPosition}`,{"wheel-container-indicator":t.displayIndicator},{"wheel-container-shadow":t.displayShadow},{"wheel-container-border":t.displayBorder}]])},[t.baseDisplay?(r(),h("div",{key:0,class:f(["wheel-base-container",[{"wheel-base-container-shadow":t.baseDisplayShadow}]]),style:k({width:`${t.baseSize}px`,height:`${t.baseSize}px`,background:`${t.baseBackground}`})},[e("div",Jt,[Q(t.$slots,"baseContent",{},void 0,!0)]),t.baseDisplayIndicator?(r(),h("div",Kt)):g("",!0)],6)):g("",!0),e("div",{class:f(["wheel",[`easing-${t.easing}`,{"wheel-border":t.displayBorder}]]),style:k({width:`${t.size}px`,height:`${t.size}px`,transitionDuration:`${t.duration}s`,transform:`rotate(${t.startingAngle}deg)`})},[(r(!0),h(I,null,A(t.items,(a,d)=>(r(),h("div",{key:a.id,class:"wheel-item",style:k({transform:`rotate(${t.itemAngle*d}deg) skewY(${-(90-t.itemAngle)}deg)`,background:a.background})},[e("div",{class:f(["content",{"horizontal-content":t.horizontalContent}]),style:k({transform:`skewY(${90-t.itemAngle}deg) rotate(${t.itemAngle/2}deg)`})},[e("span",{style:k({color:a.textColor}),innerHTML:a.htmlContent},null,12,Gt)],6)],4))),128))],6)],10,Yt)}var H=_(Tt,[["render",Zt],["__scopeId","data-v-313a8203"]]),P={items:[{id:1,name:"Banana",htmlContent:"Banana",textColor:"",background:""},{id:2,name:"Apple",htmlContent:"Apple",textColor:"",background:""},{id:3,name:"Orange",htmlContent:"Orange",textColor:"",background:""},{id:4,name:"Cherry",htmlContent:"Cherry",textColor:"",background:""},{id:5,name:"Strawberry",htmlContent:"Strawberry",textColor:"",background:""},{id:6,name:"Grape",htmlContent:"Grape",textColor:"",background:""}],firstItemIndex:{value:0},wheelSettings:{centeredIndicator:!0,indicatorPosition:"top",size:300,displayShadow:!0,duration:5,resultVariation:70,easing:"bounce",counterClockwise:!0,horizontalContent:!1,displayBorder:!0,displayIndicator:!0,baseDisplay:!0,baseSize:100,baseDisplayShadow:!0,baseDisplayIndicator:!0,baseBackground:"#EEAA33",baseHtmlContent:"Touch
Me!"}};const Qt={name:"Home",components:{Roulette:H,ItemsManager:V,WheelManager:R},data(){return B(S({},P),{wheelActive:!0,startAnim:!1,managerId:1,result:null})},mounted(){setTimeout(()=>{this.startAnim=!0},500)},methods:{launchWheel(){this.$refs.wheel.launchWheel()},wheelStartedCallback(t){console.log("wheel started !",t)},wheelEndedCallback(t){console.log("wheel ended !",t),this.result=t},onSoftReset(t){this.items=t||this.items,this.$refs.wheel.reset()},onHardReset(){this.wheelActive=!1,this.result=null,setTimeout(()=>{this.wheelActive=!0},10)}}},Xt=e("h1",{class:"text-4xl"}," Vue3 Roulette ",-1),en={class:"py-10 relative"},tn=["innerHTML"],nn={class:"absolute bottom-2 left-1/2 transform -translate-x-1/2"},on=e("p",{class:"text-xl text-gray-500 italic mb-10"}," A customizable and flexible fortune wheel made with vue3 ",-1),sn={class:"tabs tabs-boxed justify-center"},an=e("div",{class:"divider"},null,-1);function ln(t,o,s,l,n,i){const a=b("Roulette"),d=b("ItemsManager"),u=b("WheelManager");return r(),h("div",null,[Xt,e("div",en,[e("div",{class:f(["wheel-anim",{"wheel-anim-started":n.startAnim}])},[n.wheelActive?(r(),v(a,{key:0,ref:"wheel",items:t.items,"first-item-index":t.firstItemIndex,"centered-indicator":t.wheelSettings.centeredIndicator,"indicator-position":t.wheelSettings.indicatorPosition,size:t.wheelSettings.size,"display-shadow":t.wheelSettings.displayShadow,"display-border":t.wheelSettings.displayBorder,"display-indicator":t.wheelSettings.displayIndicator,duration:t.wheelSettings.duration,"result-variation":t.wheelSettings.resultVariation,easing:t.wheelSettings.easing,"counter-clockwise":t.wheelSettings.counterClockwise,"horizontal-content":t.wheelSettings.horizontalContent,"base-display":t.wheelSettings.baseDisplay,"base-size":t.wheelSettings.baseSize,"base-display-indicator":t.wheelSettings.baseDisplayIndicator,"base-display-shadow":t.wheelSettings.baseDisplayShadow,"base-background":t.wheelSettings.baseBackground,onClick:i.launchWheel,onWheelStart:i.wheelStartedCallback,onWheelEnd:i.wheelEndedCallback},{baseContent:w(()=>[t.wheelSettings.baseHtmlContent?(r(),h("div",{key:0,innerHTML:t.wheelSettings.baseHtmlContent},null,8,tn)):g("",!0)]),_:1},8,["items","first-item-index","centered-indicator","indicator-position","size","display-shadow","display-border","display-indicator","duration","result-variation","easing","counter-clockwise","horizontal-content","base-display","base-size","base-display-indicator","base-display-shadow","base-background","onClick","onWheelStart","onWheelEnd"])):g("",!0)],2),m(e("div",nn,[e("button",{class:"btn btn-xs mx-2",onClick:o[0]||(o[0]=c=>i.onHardReset())}," Hard reset "),e("button",{class:"btn btn-xs mx-2",onClick:o[1]||(o[1]=c=>i.onSoftReset())}," Soft reset ")],512),[[$,n.result]])]),on,e("div",sn,[e("a",{class:f(["tab",{"tab-active":n.managerId===1}]),onClick:o[2]||(o[2]=c=>n.managerId=1)},"Items manager",2),e("a",{class:f(["tab",{"tab-active":n.managerId===2}]),onClick:o[3]||(o[3]=c=>n.managerId=2)},"Wheel manager",2)]),an,n.managerId===1?(r(),v(d,{key:0,class:"item-manager overflow-scroll lg:overflow-auto","initial-items":t.items,"initial-first-item-index":t.firstItemIndex,onUpdateItems:i.onSoftReset},null,8,["initial-items","initial-first-item-index","onUpdateItems"])):g("",!0),n.managerId===2?(r(),v(u,{key:1,"initial-settings":t.wheelSettings,onHardReset:i.onHardReset},null,8,["initial-settings","onHardReset"])):g("",!0)])}var rn=_(Qt,[["render",ln]]);const dn=D('Vue3 Roulette
A customizable and flexible fortune wheel for vue3
Demo
https://roulette.nitocode.com/
See also: codesandbox template
Installation
Using npm
npm i --save vue3-roulette
Usage
main.js
',10),cn=e("pre",null,[e("code",{class:"language-js","v-pre":"true"},`import { createApp } from 'vue'
2 | import App from './App.vue'
3 | import { Roulette } from 'vue3-roulette'
4 |
5 | createApp(App).component("roulette", Roulette).mount('#app')
6 | `)],-1),un=e("h4",null,"vuejs html template",-1),hn=e("pre",null,[e("code",{class:"language-html","v-pre":"true"},`
7 | `)],-1),mn=e("h4",null,"vuejs script",-1),gn=e("p",null,[N("Using the "),e("a",{href:"https://v3.vuejs.org/api/sfc-script-setup.html"},"sfc syntax")],-1),pn=e("pre",null,[e("code",{class:"language-html","v-pre":"true"},`
30 |
31 |
32 |
33 |
34 |
35 |
53 |
54 |
55 |