Browse Source

feat:luckdraw转盘抽奖组件

yumingming11 5 years ago
parent
commit
286378a247

+ 1 - 1
package.json

@@ -162,4 +162,4 @@
         "instrument": false,
         "sourceMap": false
     }
-}
+}

File diff suppressed because it is too large
+ 535 - 518
src/config.json


+ 110 - 107
src/nutui.js

@@ -1,6 +1,6 @@
-import {version} from '../package.json';
-import {packages as pkgList} from './config.json';
-import {locale} from './locales';
+import { version } from '../package.json';
+import { packages as pkgList } from './config.json';
+import { locale } from './locales';
 import Cell from './packages/cell/index.js';
 import './packages/cell/cell.scss';
 import Dialog from './packages/dialog/index.js';
@@ -99,58 +99,61 @@ import LeftSlip from './packages/leftslip/index.js';
 import './packages/leftslip/leftslip.scss';
 import TabSelect from "./packages/tabselect/index.js";
 import "./packages/tabselect/tabselect.scss";
-
 import './packages/popup/popup.scss';
+import LuckDraw from "./packages/luckdraw/index.js";
+import "./packages/luckdraw/luckdraw.scss";
+
 const packages = {
-    Cell,
-    Dialog,
-    Icon,
-    Toast,
-    ActionSheet,
-    Tab,
-    TabPanel,
-    TabBar,
-    Calendar,
-    DatePicker,
-    NavBar,
-    NoticeBar,
-    Switch,
-    Slider,
-    Range,
-    Picker,
-    Progress,
-    Price,
-    Flex,
-    Col,
-    Row,
-    Steps,
-    Button,
-    Badge,
-    Rate,
-    Swiper,
-    Menu,
-    Stepper,
-    ButtonGroup,
-    SearchBar,
-    ImagePicker,
-    Radio,
-    RadioGroup,
-    CheckBox,
-    CheckBoxGroup,
-    ShortPassword,
-    Skeleton,
-    Scroller,
-    BackTop,
-    CountDown,
-    InfiniteLoading,
-    Uploader,
-    TextInput,
-    TextBox,
-    Avatar,
-    Elevator,
-    Popup,
-    LeftSlip,
-    TabSelect: TabSelect
+  Cell,
+  Dialog,
+  Icon,
+  Toast,
+  ActionSheet,
+  Tab,
+  TabPanel,
+  TabBar,
+  Calendar,
+  DatePicker,
+  NavBar,
+  NoticeBar,
+  Switch,
+  Slider,
+  Range,
+  Picker,
+  Progress,
+  Price,
+  Flex,
+  Col,
+  Row,
+  Steps,
+  Button,
+  Badge,
+  Rate,
+  Swiper,
+  Menu,
+  Stepper,
+  ButtonGroup,
+  SearchBar,
+  ImagePicker,
+  Radio,
+  RadioGroup,
+  CheckBox,
+  CheckBoxGroup,
+  ShortPassword,
+  Skeleton,
+  Scroller,
+  BackTop,
+  CountDown,
+  InfiniteLoading,
+  Uploader,
+  TextInput,
+  TextBox,
+  Avatar,
+  Elevator,
+  Popup,
+  LeftSlip,
+  TabSelect: TabSelect,
+  LuckDraw: LuckDraw
 };
 
 const components = {};
@@ -158,79 +161,79 @@ const methods = {};
 const filters = {};
 const directives = {};
 pkgList.map(item => {
-    const pkg = packages[item.name];
-    if (!pkg) return;
+  const pkg = packages[item.name];
+  if (!pkg) return;
 
-    if (item.type == 'component') {
-        if (pkg.name) {
-            components[pkg.name] = pkg;
-        } else {
-            for (let n in pkg) {
-                components[n] = pkg[n];
-            }
-        }
-    } else if (item.type == 'method') {
-        methods[item.name] = pkg;
-    } else if (item.type == 'filter') {
-        filters[item.name] = pkg;
-    } else if (item.type == 'directive') {
-        directives[item.name] = pkg;
+  if (item.type == 'component') {
+    if (pkg.name) {
+      components[pkg.name] = pkg;
+    } else {
+      for (let n in pkg) {
+        components[n] = pkg[n];
+      }
     }
+  } else if (item.type == 'method') {
+    methods[item.name] = pkg;
+  } else if (item.type == 'filter') {
+    filters[item.name] = pkg;
+  } else if (item.type == 'directive') {
+    directives[item.name] = pkg;
+  }
 });
 
-const install = function(Vue, opts = {}) {
-    if (install.installed) return;
+const install = function (Vue, opts = {}) {
+  if (install.installed) return;
 
-    if (opts.locale) {
-        Vue.config.lang = opts.locale;
-    }
+  if (opts.locale) {
+    Vue.config.lang = opts.locale;
+  }
 
-    if (opts.lang) locale(Vue.config.lang, opts.lang);
+  if (opts.lang) locale(Vue.config.lang, opts.lang);
 
-    for (let cptName in methods) {
-        if (Array.isArray(methods[cptName])) {
-            Vue.prototype['$' + cptName.toLowerCase()] = methods[cptName][0];
-            Vue.component(methods[cptName][1].name, methods[cptName][1]);
-        } else {
-            Vue.prototype['$' + cptName.toLowerCase()] = methods[cptName];
-        }
+  for (let cptName in methods) {
+    if (Array.isArray(methods[cptName])) {
+      Vue.prototype['$' + cptName.toLowerCase()] = methods[cptName][0];
+      Vue.component(methods[cptName][1].name, methods[cptName][1]);
+    } else {
+      Vue.prototype['$' + cptName.toLowerCase()] = methods[cptName];
     }
+  }
 
-    for (let cptName in components) {
-        if (components[cptName] && components[cptName].name) {
-            Vue.component(components[cptName].name, components[cptName]);
-        }
+  for (let cptName in components) {
+    if (components[cptName] && components[cptName].name) {
+      Vue.component(components[cptName].name, components[cptName]);
     }
+  }
 
-    for (let cptName in filters) {
-        if (filters[cptName] && filters[cptName].name) {
-            Vue.filter(cptName, filters[cptName]);
-        }
+  for (let cptName in filters) {
+    if (filters[cptName] && filters[cptName].name) {
+      Vue.filter(cptName, filters[cptName]);
     }
+  }
 
-    for (let cptName in directives) {
-        if (directives[cptName] && directives[cptName].name) {
-            Vue.directive(directives[cptName].name, directives[cptName]);
-        }
+  for (let cptName in directives) {
+    if (directives[cptName] && directives[cptName].name) {
+      Vue.directive(directives[cptName].name, directives[cptName]);
     }
+  }
 
-    Vue.use(Lazyload, {
-        lazyComponent: true,
-        loading: '//img12.360buyimg.com/imagetools/jfs/t1/73967/28/14561/916/5dc142e4E0666555b/bf33454553c6035e.png'
-    });
+  Vue.use(Lazyload, {
+    lazyComponent: true,
+    loading: '//img12.360buyimg.com/imagetools/jfs/t1/73967/28/14561/916/5dc142e4E0666555b/bf33454553c6035e.png'
+  });
 };
 
 if (typeof window !== 'undefined' && window.Vue) {
-    install(window.Vue);
+  install(window.Vue);
 }
 
 export default {
-    version,
-    locale,
-    install,
-    Lazyload,
-    ...components,
-    ...filters,
-    ...directives,
-    ...methods
+  version,
+  locale,
+  install,
+  Lazyload,
+  ...components,
+  ...filters,
+  ...directives,
+  ...methods
 };

+ 196 - 0
src/packages/luckdraw/demo.vue

@@ -0,0 +1,196 @@
+<template>
+  <div class="luckDrawBox">
+    <h4>基本用法</h4>
+    <div>
+      <nut-cell>
+        <span slot="title">剩余抽奖次数:{{ num }}</span>
+      </nut-cell>
+    </div>
+    <nut-luckdraw
+      class="drawTable"
+      ref="luckDrawPrize"
+      :luckWidth="luckWidth"
+      :luckheight="luckheight"
+      :prizeList="prizeList"
+      :turnsNumber="turnsNumber"
+      :turnsTime="turnsTime"
+      :prizeIndex="prizeIndex"
+      :styleOpt="styleOpt"
+      @endTurns="endTurns"
+    >
+      <template slot="item" slot-scope="scope">
+        <div class="drawTable-name">{{ scope.item.prizeName }}</div>
+        <div class="drawTable-img">
+          <img :src="scope.item.prizeImg">
+        </div>
+      </template>
+    </nut-luckdraw>
+    <div @click="startTurns" class="pointer" :style="pointerStyle"></div>
+  </div>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        // 转盘大小
+        luckWidth: '300px',
+        luckheight: '300px',
+        // 转盘指针图片样式
+        pointerStyle: {
+          width: '80px',
+          height: '80px',
+          backgroundImage: 'url("https://img11.360buyimg.com/imagetools/jfs/t1/106989/15/11126/137350/5e265414E8ee514bc/3456bd0d3a0454da.png")',
+          backgroundSize: 'contain',
+          backgroundRepeat: 'no-repeat',
+        },
+        // 转盘上要展示的奖品数据
+        prizeList: [
+          {
+            id: 'xiaomi',
+            prizeName: '小米手机',
+            prizeImg: '//m.360buyimg.com/mobilecms/s843x843_jfs/t1/96788/40/337/73706/5dabd0e2E1f166028/7120ca2b421cb0a0.jpg!q70.dpg.webp',
+          },
+          {
+            id: 'blue',
+            prizeName: '蓝牙耳机',
+            prizeImg: '//m.360buyimg.com/mobilecms/s843x843_jfs/t1/65070/13/4325/183551/5d26e23fE09ab2010/a94eaff8242e6c63.jpg!q70.dpg.webp',
+          },
+          {
+            id: 'apple',
+            prizeName: 'apple watch',
+            prizeImg: '//m.360buyimg.com/mobilecms/s843x843_jfs/t1/105083/3/4010/126031/5de4aa51E1c7fefc6/0288f4cf3016e061.jpg!q70.dpg.webp',
+          },
+          {
+            id: 'fruit',
+            prizeName: '迪士尼苹果',
+            prizeImg: '//m.360buyimg.com/mobilecms/s750x750_jfs/t1/47486/35/13399/356858/5da3cde2E9b3ec40f/3b3a56d54d5db565.jpg!q80.dpg.webp',
+          },
+          {
+            id: 'fish',
+            prizeName: '海鲜套餐',
+            prizeImg: '//m.360buyimg.com/mobilecms/s843x843_jfs/t1/109529/24/1330/283533/5dfc836fE33d8ce6b/372adb638802710a.jpg!q70.dpg.webp',
+          },
+          {
+            id: 'thanks',
+            prizeName: '谢谢参与',
+            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/104502/28/10892/5123/5e265414Ec167392c/2831c6155895f33d.png',
+          }
+        ],
+        // 转动圈数
+        turnsNumber: 5,
+        // 转动需要持续的时间(秒)
+        turnsTime: 5,
+        // 转盘样式的选项
+        styleOpt: {
+          // 转盘中每一块扇形的背景色,根据奖品的index来取每一块的对应颜色
+          prizeBgColors: ['rgb(255, 231, 149)','rgb(255, 247, 223)','rgb(255, 231, 149)','rgb(255, 247, 223)','rgb(255, 231, 149)','rgb(255, 247, 223)'],
+          // 每一个扇形的外边框颜色
+          borderColor: '#ff9800',
+        },
+        // 中奖的奖品的index(此数据可根据后台返回的值重新赋值)
+        prizeIndex: -1,
+        // 用来锁定转盘,避免同时多次点击转动
+        lock: false,
+        // 剩余抽奖次数
+        num: 5,
+      }
+    },
+    methods: {
+      startTurns() {
+        // 如果还不可以转动
+        if (!this.canBeRotated()) {
+          return false;
+        }
+        // 开始转动
+        // 先上锁
+        this.lock = true;
+        // 设置在哪里停下,应该与后台交互,这里随机抽取0~5 ,这里应该是后台返回的中奖信息,现在是测试
+        const index = Math.floor(Math.random() * this.prizeList.length);
+        // 成功后次数减少一次
+        this.num--;
+        this.prizeIndex = index;
+        // 告诉子组件,开始转动了
+        this.$refs.luckDrawPrize.rotate(index);
+      },
+      // 已经转动完转盘触发的函数
+      endTurns() {
+        // 提示中奖
+        this.$dialog({
+          content: `恭喜中奖!!!${this.prizeList[this.prizeIndex].prizeName}`,
+          noCloseBtn: false,
+          noOkBtn: true,
+          cancelBtnTxt: "我知道了"
+        });
+        // 解锁
+        this.lock = false;
+      },
+      // 判断是否可以转动
+      canBeRotated() {
+        if (this.num <= 0) {
+          alert('已经没有次数了,继续加油赚积分吧!');
+          this.$dialog({
+            content: `已经没有次数了,继续加油赚积分吧!`,
+            noCloseBtn: false,
+            noOkBtn: true,
+            cancelBtnTxt: "我知道了"
+          });
+          return false;
+        }
+        if (this.lock) {
+          return false;
+        }
+        return true;
+      },
+    }
+  }
+</script>
+
+<style lang="scss">
+.luckDrawBox {
+  h3 {
+    width: 100%;
+    text-align: center;
+    font-size: 24px;
+  }
+  // .drawTable {
+  //   position: absolute;
+  //   left: 50%;
+  //   top: 50%;
+  //   transform: translate(-50%, -50%);
+  // }
+  // .drawTable-name {
+  //   position: absolute;
+  //   left: 10px;
+  //   top: 20px;
+  //   width: calc(100% - 20px);
+  //   font-size: 12px;
+  //   text-align: center;
+  //   color: #ff5722;
+  // }
+  // .drawTable-img {
+  //   position: absolute;
+  //   /*要居中就要50% - 宽度 / 2*/
+  //   left: calc(50% - 30px / 2);
+  //   top: 60px;
+  //   width: 30px;
+  //   height: 30px;
+  //   img {
+  //     display: inline-block;
+  //     width: 100%;
+  //     height: 100%;
+  //   }
+  // }
+  // .pointer {
+  //   position: absolute;
+  //   // left: calc(50% - 35px);
+  //   // top: calc(50% - 40px);
+  //   left: 50%;
+  //   top: 50%;
+  //   transform: translate(-43.75%, -50%);
+  //   // background-image: url("https://img11.360buyimg.com/imagetools/jfs/t1/106989/15/11126/137350/5e265414E8ee514bc/3456bd0d3a0454da.png");
+  //   // background-size: contain;
+  //   // background-repeat: no-repeat;
+  // }
+}
+</style>

+ 150 - 0
src/packages/luckdraw/doc.md

@@ -0,0 +1,150 @@
+# LuckDraw 转盘抽奖
+
+## 基本用法
+
+```html
+<luck-draw
+  class="drawTable"
+  ref="luckDrawPrize"
+  :luckWidth="luckWidth"
+  :luckheight="luckheight"
+  :prizeList="prizeList"
+  :turnsNumber="turnsNumber"
+  :turnsTime="turnsTime"
+  :prizeIndex="prizeIndex"
+  :styleOpt="styleOpt"
+  @endTurns="endTurns"
+>
+  <template slot="item" slot-scope="scope">
+    <div class="drawTable-name">{{ scope.item.prizeName }}</div>
+    <div class="drawTable-img">
+      <img :src="scope.item.prizeImg">
+    </div>
+  </template>
+</luck-draw>
+<div @click="startTurns" class="pointer" :style="pointerStyle"></div>
+```
+
+```javascript
+export default {
+    data() {
+      return {
+        // 转盘大小
+        luckWidth: '300px',
+        luckheight: '300px',
+        // 转盘指针图片样式
+        pointerStyle: {
+          width: '80px',
+          height: '80px',
+          backgroundImage: 'url("https://img11.360buyimg.com/imagetools/jfs/t1/106989/15/11126/137350/5e265414E8ee514bc/3456bd0d3a0454da.png")',
+          backgroundSize: 'contain',
+          backgroundRepeat: 'no-repeat',
+        },
+        // 奖品列表
+        prizeList: [
+          {
+            id: 'xiaomi',
+            prizeName: '小米手机',
+            prizeImg: '//m.360buyimg.com/mobilecms/s843x843_jfs/t1/96788/40/337/73706/5dabd0e2E1f166028/7120ca2b421cb0a0.jpg!q70.dpg.webp',
+          },
+          {
+            id: 'blue',
+            prizeName: '蓝牙耳机',
+            prizeImg: '//m.360buyimg.com/mobilecms/s843x843_jfs/t1/65070/13/4325/183551/5d26e23fE09ab2010/a94eaff8242e6c63.jpg!q70.dpg.webp',
+          },
+          {
+            id: 'apple',
+            prizeName: 'apple watch',
+            prizeImg: '//m.360buyimg.com/mobilecms/s843x843_jfs/t1/105083/3/4010/126031/5de4aa51E1c7fefc6/0288f4cf3016e061.jpg!q70.dpg.webp',
+          },
+          {
+            id: 'fruit',
+            prizeName: '迪士尼苹果',
+            prizeImg: '//m.360buyimg.com/mobilecms/s750x750_jfs/t1/47486/35/13399/356858/5da3cde2E9b3ec40f/3b3a56d54d5db565.jpg!q80.dpg.webp',
+          },
+          {
+            id: 'fish',
+            prizeName: '海鲜套餐',
+            prizeImg: '//m.360buyimg.com/mobilecms/s843x843_jfs/t1/109529/24/1330/283533/5dfc836fE33d8ce6b/372adb638802710a.jpg!q70.dpg.webp',
+          },
+          {
+            id: 'thanks',
+            prizeName: '谢谢参与',
+            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/104502/28/10892/5123/5e265414Ec167392c/2831c6155895f33d.png',
+          }
+        ],
+        turnsNumber: 5, // 转动圈数
+        turnsTime: 5,// 转动时间:S
+        styleOpt: {
+          prizeBgColors: ['rgb(255, 231, 149)','rgb(255, 247, 223)','rgb(255, 231, 149)','rgb(255, 247, 223)','rgb(255, 231, 149)','rgb(255, 247, 223)'],
+          borderColor: '#ff9800',
+        },
+        prizeIndex: -1, // 中奖奖品的index
+        lock: false,// 防止多次连续点击抽奖
+        num: 5,// 抽奖次数,根据需求定义
+      }
+    },
+    methods: {
+      startTurns() {
+        if (!this.canBeRotated()) {
+          return false;
+        }
+        this.lock = true;
+        // 此为模拟随机数字,这里可以接受后台中奖信息
+        const index = Math.floor(Math.random() * this.prizeList.length);
+        // 成功后抽奖次数减1
+        this.num--;
+        // 中奖奖品的index
+        this.prizeIndex = index;
+        // 调用组件的方法,使转盘转动并停留在中奖奖品的那个扇形区域
+        this.$refs.luckDrawPrize.rotate(index);
+      },
+      endTurns() {
+        this.$dialog({
+          content: `恭喜中奖!!!${this.prizeList[this.prizeIndex].prizeName}`,
+          noCloseBtn: false,
+          noOkBtn: true,
+          cancelBtnTxt: "我知道了"
+        });
+        this.lock = false;
+      },
+      canBeRotated() {
+        if (this.num <= 0) {
+          this.$dialog({
+            content: `已经没有次数了,继续加油赚积分吧!`,
+            noCloseBtn: false,
+            noOkBtn: true,
+            cancelBtnTxt: "我知道了"
+          });
+          return false;
+        }
+        if (this.lock) {
+          return false;
+        }
+        return true;
+      },
+    }
+  }
+```
+
+
+## Prop
+
+| 字段 | 说明 | 类型 | 默认值
+|----- | ----- | ----- | ----- 
+| ref | 当前转盘的类名,转动的时候根据类名执行回调函数 | String | luckDrawPrize
+| luckWidth | 转盘的宽度 | String | 300px
+| luckHeight | 转盘的高度 | String | 300px
+| prizeList | 奖品列表 | Array | -
+| turnsNumber | 转动的圈数 | Number | 5
+| turnsTime | 从开始转动到结束所用时间 | Number | 5(单位是秒)
+| styleOpt | 转盘中的样式,包括每个扇区的背景颜色,扇区的边框颜色 | Object | {prizeBgColors: [],borderColor: ''}
+| pointerStyle | 转盘中指针的样式,包括背景图片、大小等 | Object | {width: '80px',height: '80px'}
+
+
+
+## Event
+
+| 字段 | 说明 | 回调参数
+|----- | ----- | -----
+| endTurns | 转盘中停止转动后的回调函数 | - 

+ 8 - 0
src/packages/luckdraw/index.js

@@ -0,0 +1,8 @@
+import LuckDraw from './luckdraw.vue';
+import './luckdraw.scss';
+
+LuckDraw.install = function(Vue) {
+  Vue.component(LuckDraw.name, LuckDraw);
+};
+
+export default LuckDraw

+ 60 - 0
src/packages/luckdraw/luckdraw.scss

@@ -0,0 +1,60 @@
+.nut-luckdraw{
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    text-align: center;
+    // transform: translateZ(0);
+    .drawTable-name {
+        position: absolute;
+        left: 10px;
+        top: 20px;
+        width: calc(100% - 20px);
+        font-size: 12px;
+        text-align: center;
+        color: #ff5722;
+      }
+      .drawTable-img {
+        position: absolute;
+        /*要居中就要50% - 宽度 / 2*/
+        left: calc(50% - 30px / 2);
+        top: 60px;
+        width: 30px;
+        height: 30px;
+        img {
+          display: inline-block;
+          width: 100%;
+          height: 100%;
+        }
+      }
+    .lucktable {
+        position: absolute;
+        left: 0;
+        top: 0;
+        width: 100%;
+        height: 100%;
+    }
+    .prize {
+        position: absolute;
+        left: 25%;
+        top: 0;
+        width: 50%;
+        height: 50%;
+        .item {
+            position: absolute;
+            left: 0;
+            top: 0;
+            width: 100%;
+            height: 100%;
+            transform-origin: center bottom;
+        }
+    }
+}
+.pointer {
+    position: absolute;
+    // left: calc(50% - 35px);
+    // top: calc(50% - 40px);
+    left: 50%;
+    top: 50%;
+    transform: translate(-43.75%, -50%);
+}

+ 113 - 0
src/packages/luckdraw/luckdraw.vue

@@ -0,0 +1,113 @@
+<template>
+    <div class="nut-luckdraw" ref="luckdraw" :style="{width:luckWidth, height:luckheight}">
+        <div class="lucktable" :style="{transform: rotateAngle, transition: rotateTransition}">
+            <canvas id="canvas" ref="canvas">
+                浏览器版本过低
+            </canvas>
+            <div class="prize">
+                <div v-for="(item, index) in prizeList" class="item" :style="getRotateAngle(index)" :key="index">
+                <slot name="item" :item="item"></slot>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+export default {
+    name:'nut-luckdraw',
+    mounted() {
+      this.init();
+    },
+    props: {
+      luckWidth: {
+        required: true
+      },
+      luckheight: {
+        required: true
+      },
+      prizeList: {
+        required: true
+      },
+      turnsNumber: {
+        default: 5
+      },
+      styleOpt: {
+        default: () => {
+          return {
+            // 每一块扇形的背景色,默认值,可通过父组件来改变
+            prizeBgColors: ['rgb(255, 231, 149)','rgb(255, 247, 223)','rgb(255, 231, 149)','rgb(255, 247, 223)','rgb(255, 231, 149)','rgb(255, 247, 223)'],
+            // 每一块扇形的外边框颜色,默认值,可通过父组件来改变
+            borderColor: '#ff9800',
+          }
+        }
+      },
+      turnsTime: {
+        // 转动需要持续的时间(秒)  
+        default: 5
+      }
+    },
+    data() {
+      return {
+        winningPrize: 0,
+        // 开始转动的角度
+        startRotateDegree: 0,
+        // 设置指针默认指向的位置,现在是默认指向2个扇形之间的边线上
+        rotateAngle: 0,//`rotate(30deg)`
+        rotateTransition: '',
+      }
+    },
+    methods: {
+      // 根据index计算每一格要旋转的角度的样式
+      getRotateAngle(index) {
+        const angle = 360 / this.prizeList.length * index + (180 / this.prizeList.length);
+        return {
+          transform: `rotate(${angle}deg)`
+        };
+      },
+      // 初始化圆形转盘canvas
+      init() {
+        const data = this.styleOpt;
+        const prizeNum = this.prizeList.length;
+        const {prizeBgColors, borderColor} = data;
+        // 开始绘画
+        const canvas = this.$refs.canvas;
+        const ctx = canvas.getContext("2d");
+        const canvasW = this.$refs.canvas.width = this.$refs.luckdraw.clientWidth; // 画板的高度
+        const canvasH = this.$refs.canvas.height = this.$refs.luckdraw.clientHeight; // 画板的宽度
+        // translate方法重新映射画布上的 (0,0) 位置
+        ctx.translate(0, canvasH);
+        // rotate方法旋转当前的绘图,因为文字是和当前扇形中心线垂直的
+        ctx.rotate(-90 * Math.PI / 180);
+        // 圆环的外圆的半径,可用来调整圆盘大小来适应外部盒子的大小
+        const outRadius = canvasW / 2 - 1;
+        // 圆环的内圆的半径
+        const innerRadius = 0;
+        const baseAngle = Math.PI * 2 / prizeNum; // 每个奖项所占角度数
+        ctx.clearRect(0, 0, canvasW, canvasH); //去掉背景默认色
+        ctx.strokeStyle = borderColor; // 设置画图线的颜色
+        for (let index = 0; index < prizeNum; index++) {
+          const angle = index * baseAngle;
+          ctx.fillStyle = prizeBgColors[index]; //设置每个扇形区域的颜色
+          ctx.beginPath(); //开始绘制
+          // 标准圆弧:arc(x,y,radius,startAngle,endAngle,anticlockwise)
+          ctx.arc(canvasW * 0.5, canvasH * 0.5, outRadius, angle, angle + baseAngle, false);
+          ctx.arc(canvasW * 0.5, canvasH * 0.5, innerRadius, angle + baseAngle, angle, true);
+          ctx.stroke();
+          ctx.fill();
+          ctx.save();
+        }
+      },
+      // 转动起来
+      rotate(index) {
+        const turnsTime = this.turnsTime;
+        const rotateAngle = this.startRotateDegree + this.turnsNumber * 360 + 360 - (180 / this.prizeList.length + 360 / this.prizeList.length * index) - this.startRotateDegree % 360;
+        this.startRotateDegree = rotateAngle;
+        this.rotateAngle = `rotate(${rotateAngle}deg)`;
+        this.rotateTransition = `transform ${turnsTime}s cubic-bezier(0.250, 0.460, 0.455, 0.995)`;
+        setTimeout(() => {
+          this.$emit('endTurns');
+        }, turnsTime * 1000 + 500)
+      },
+    },
+}
+</script>

+ 1 - 0
types/nutui.d.ts

@@ -68,3 +68,4 @@ export declare class LeftSlip extends UIComponent {}
 export declare class TabSelect extends UIComponent {}
 export declare class Popup extends UIComponent {}
 
+export declare class LuckDraw extends UIComponent {}