微信小程序中自定义组件的使用-本文以微信小程序抽奖自定义组件为例进行说明

自定义组件

新建文件夹component

在这个文件夹下面设置自定义的组件(抽奖组件)

组件截图如下:

微信小程序中自定义组件的使用-本文以微信小程序抽奖自定义组件为例进行说明

cj.json文件

{

  "component": true,

  "usingComponents": {}

}

cj.wxml文件

<view class="draw_num">您还有<text>{{num}}</text>次抽奖机会</view>

<view class="canvas-container">

   <view animation="{{animationData}}" class="gb-wheel-content" style='width:{{size-2}}rpx;height:{{size}}rpx;'>

      <!-- 扇形颜色背景 当选项长度等于2或者3时做了特殊处理 -->

      <view class="canvas-list">

         <view class="canvas-item2" 

               wx:for="{{awardsConfig.awards}}" 

               wx:key="key" 

               style="transform: rotate({{item.item2Deg}});background-color:{{awardsConfig.awards.length==2?item.color:''}};opacity:{{awardsConfig.awards.length==2?item.opacity:awardsConfig.awards.length==3?item.opacity:''}};width:{{size}}rpx;height:{{size/2-2}}rpx;transform-origin:{{size/2}}rpx {{size/2}}rpx">

            <view class="canvas-item2-after" 

                  style="transform: rotate({{item.afterDeg}});background-color:{{item.color}};opacity:{{awardsConfig.awards.length==3?'':item.opacity}};width:{{size/2}}rpx;height:{{size/2}}rpx;transform-origin: {{size/2}}rpx {{size/2}}rpx"></view>

            <view wx:if='{{awardsConfig.awards.length==3}}' 

                  class="canvas-item2-after" 

                  style="background-color:{{item.color}};width:{{size/2}}rpx;height:{{size/2}}rpx;transform-origin: {{size/2}}rpx {{size/2}}rpx"></view>

         </view>

      </view>

 

      <!-- 选项内容 -->

      <view class="gb-wheel-list">

         <view class="gb-wheel-item" 

               data-index="{{index}}" 

               wx:for="{{awardsConfig.awards}}" 

               wx:key='key'>

            <view class="gb-wheel-icontent" 

                  style="height:262rpx;overflow:hidden;font-size:{{item.name.length>9?'26':'30'}}rpx;line-height:{{item.name.length>9?'26':'30'}}rpx;width:{{awardsConfig.awards.length > 10 ? '140rpx':'200rpx'}};padding-top:25rpx;transform: rotate({{index*turnNum}}turn);transform-origin: 50% {{size/2-2}}rpx">

               <text style='word-break:break-all;display:block;'>{{item.name}}</text>

               <image src="{{item.img}}" style="width:80rpx;height:100rpx;margin-top:10rpx"></image>

            </view>

         </view>

      </view>

   </view>

 

   <view class="img-container" style='width:100%;height:{{size}}rpx;'>

      <!-- 转盘中间的按钮 -->

      <image bindtap="_zhuan" 

             src='../images/canvas_button_go_unclick.png' 

             style='width:{{size/4.4}}rpx;display:{{block1}};margin-top:-49rpx;left:15rpx;' 

             mode='widthFix'></image>

      <image src='../images/canvas_button_go_click.png' 

             style='width:{{size/4.4}}rpx;display:{{block2}};margin-top:-49rpx;left:15rpx;' 

             mode='widthFix'></image>

      <image bindtap="reset" 

             src='../images/canvas_button_reset_unclick.png' 

             style='width:{{size/4.4}}rpx;display:{{block3}};margin-top:-49rpx;left:15rpx;' 

             mode='widthFix'></image>

      <!-- <image src='../../images/canvas_button_reset_click.png' 

             style='width:{{size/4.4}}rpx;display:{{block4}};margin-top:16rpx' 

             mode='widthFix'></image> -->

   </view>

</view>

cj.wxss文件

.draw_num{

  text-align:center;

  padding-top:-21rpx;

  padding-bottom:21rpx;

  font-size:30rpx;

  font-weight:600;

}

.draw_num text{

  color:#f00;

  padding:0 7rpx;

}

.canvas-container {

  margin: 0 auto;

  position: relative;

  display: flex;

  align-items: center;

  justify-content: center;

}

 

.img-container {

  margin: 0 auto;

  position: absolute;

  display: flex;

  align-items: center;

  justify-content: center;

  left: 0;

  top: 0;

  z-index:10;

}

 

.gb-wheel-run {

  box-shadow: 0 0 5rpx 0rpx rgba(0, 0, 0, 0.98);

  width: 700rpx;

  height: 700rpx;

  border-radius: 50%;

  border: 30rpx solid #f1ecec;

  box-sizing: border-box;

  position: absolute;

  left: 27rpx;

  top: -19rpx;

  opacity: 0.7;

}

 

.gb-wheel-content {

  position: relative;

  margin: 0 auto;

  z-index: 2;

  width: 660rpx;

  height: 660rpx;

  border-radius: 50%;

  border: 20rpx solid #f1ecec;

  box-shadow: 0 0 5rpx 0rpx rgba(0, 0, 0, 0.98);

  opacity: 0.7;

  overflow: hidden;

}

 

.canvas-list {

  position: absolute;

  left: 0;

  top: 0;

  width: inherit;

  height: inherit;

  z-index: 8;

}

 

.canvas-item2 {

  position: absolute;

  left: 0px;

  top: 0;

  width: 660rpx;

  height: 328rpx;

  color: #e4370e;

  font-weight: bold;

  transform-origin: 330rpx 330rpx;

  overflow: hidden;

}

 

.canvas-item2-after {

  position: absolute;

  top: 0;

  left: 0;

  width: 330rpx;

  height: 330rpx;

  transform-origin: 330rpx 330rpx;

  opacity: 1;

}

 

.gb-wheel-list {

  position: absolute;

  left: 0;

  top: 0;

  width: 100%;

  height: 100%;

  z-index: 9;

}

 

.gb-wheel-item {

  position: absolute;

  left: 0;

  top: 0;

  width: 100%;

  height: 100%;

  color: #fff;

  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.6);

}

 

.gb-wheel-icontent {

  position: relative;

  display: block;

  padding-top: 50rpx;

  margin: 0 auto;

  text-align: center;

  transform-origin: 50% 328rpx;

}

cj.js文件

// pages/cj/cj.js

//创建并返回内部 audio 上下文 innerAudioContext 对象

const start = wx.createInnerAudioContext();

const mid = wx.createInnerAudioContext();

const stop = wx.createInnerAudioContext();

 

Component({

  options: {

    multipleSlots: true // 在组件定义时的选项中启用多slot支持

  },

 

  /**

   * 组件的属性列表

   * 用于组件自定义设置   组件的对外属性

   */

  properties: {

    myProperty: {    // 属性名        myProperty2: String, 简化的定义方式

      type: String, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型)

      value: '',    // 属性默认 初始值(可选),如果未指定则会根据类型选择一个

      observer: function (newVal, oldVal, changedPath) {

        // 属性被改变时执行的函数(可选),也可以写成在methods段中定义的方法名字符串, 如:'_propertyChange'

        // 通常 newVal 就是新设置的数据, oldVal 是旧数据

      }

    },

    num:{

      type: Number,

      value:0,

      observer: function (newVal, oldVal, changedPath) {

        this.setData({

          num:newVal

        })

      }

    },

    probability: {

      type: Boolean, // 概率开关,默认false 随机

      value: true

    },

 

    musicflg: {

      type: Boolean, // 转盘声音开关,默认true

      value: true

    },

 

    fastJuedin: {

      type: Boolean, // 快速转动转盘的开关,默认false

      value: false

    },

 

    repeat: {

      type: Boolean, // 重复抽取开关,默认false

      value: false

    },

 

    size: {

      type: Number, // 转盘大小,传入宽度即可

      value: 600

    },

 

    zhuanpanArr: { // 可以切换的转盘选项, 支持多个

      type: Array,

      value: [

        {

          id: 0,

          option: '转盘的标题名称',

          awards: [

            {

              id: 0,

              name: "1",  // 选项名

              color: 'red',         // 选项的背景颜色

              probability: 10       // 概率 0代表永远也转不到这个选项,数字越大概率也就越大

            },

            {

              id: 1,

              name: "2", // 超过9个字时字体会变小点

              color: 'green',

              probability: 10

            },

            {

              id: 2,

              name: "3", // 超过9个字时字体会变小点

              color: 'blue',

              probability: 10

            },

            {

              id: 3,

              name: "4", // 超过9个字时字体会变小点

              color: 'pink',

              probability: 10

            },

            {

              id: 4,

              name: "5", // 超过9个字时字体会变小点

              color: 'orange',

              probability: 10

            }

          ]

        }

      ],

      observer: function (newVal, oldVal, changedPath) {

        // 属性被改变时执行的函数(可选),也可以写成在methods段中定义的方法名字符串, 如:'_propertyChange'

        // 通常 newVal 就是新设置的数据, oldVal 是旧数据

        // console.log(oldVal);

        this.setData({

          zhuanpanArr:newVal

        })

        console.log(this.data.zhuanpanArr);

      }

    },

 

    // 限制:最多17个选项, 单个选项最多填10-13个字,多余部分会隐藏

    awardsConfig: { // 默认的当前转盘选项 

      type: Object,

      value: {

        option: '我的小决定?',

        awards: [

          {

            id: 0,

            name: "奖项一",

            color: 'red',

            probability: 0

          },

          {

            id: 1,

            name: "选项二",

            color: 'green',

            probability: 0

          },

          {

            id: 2,

            name: "选项三", 

            color: 'blue',

            probability: 10

          }

        ],

      },

      observer: function (newVal, oldVal, changedPath) {

        console.log(newVal);

        if (newVal) {

          this.switchZhuanpan(newVal, true);

        }

      }

    }

 

  },

 

  /**

   * 私有数据,组件的初始数据

   * 可用于模版渲染   

   */

  data: {

    animationData: {}, // 转盘动画

    zhuanflg: false,   // 转盘是否可以点击切换的标志位

    fastTime: 7600,    // 转盘快速转动的时间

    slowTime: 3900,    // 转盘慢速转动的时间

    runDegs: 0,        // 转盘旋转了多少圈

    timer: null,       // 清除转盘的时间clearTimeout(timer)

    block1: 'block',   // 控制显示或隐藏转盘中心的图片

    block2: 'none',

    block3: 'none',

    block4: 'none'

  },

 

  //组件生命周期函数,在组件实例进入页面节点树时执行,注意此时不能调用 setData

  created: function () { },

 

  // 组件生命周期函数,在组件实例进入页面节点树时执行

  attached: function () {

    console.log('===============attached===============');

    start.src = 'https://gamesdata.oss-cn-hangzhou.aliyuncs.com/xiaojueding/start.mp3'; // 转盘开始转动的音乐

    mid.src = 'https://gamesdata.oss-cn-hangzhou.aliyuncs.com/xiaojueding/mid.mp3';     // 快速决定时,转盘开始转动的音乐

    stop.src = 'https://gamesdata.oss-cn-hangzhou.aliyuncs.com/xiaojueding/stop.mp3';   // 转盘停止转动的音乐

 

    this.setData({

      awardsConfig: this.data.zhuanpanArr[0]

    })

    // this.initAdards();

  },

 

  /**

   * 组件的方法列表

   * 更新属性和数据的方法与更新页面数据的方法类似

   */

  methods: {

    /*

     * 公有方法

     */

    //判断值是否为空

    isNull(str) {

      if (str == null || str == undefined || str == '') {

        return true;

      } else {

        return false;

      }

    },

 

    //初始化数据

    initAdards() {

      var that = this, awardsConfig = this.data.awardsConfig;

      console.log('lotteryData', awardsConfig)

      var t = awardsConfig.awards && awardsConfig.awards.length || 0;  // 选项长度

      var e = 1 / t, i = 360 / t, r = i - 90;

 

      for (var g = 0; g < t; g++) {

        awardsConfig.awards[g].item2Deg = g * i + 90 - i / 2 + "deg";//当前下标 * 360/长度 + 90 - 360/长度/2

        awardsConfig.awards[g].afterDeg = r + "deg";

        awardsConfig.awards[g].opacity = '1';

      }

 

      that.setData({

        turnNum: e, // 页面的单位是turn

        awardsConfig: awardsConfig,

      })

 

      that._change();//向父组件传出当前转盘的初始数据

    },

 

    //重置转盘

    reset() {

      var that = this, awardsConfig = that.data.awardsConfig;

      var animation = wx.createAnimation({

        duration: 1,

        timingFunction: "ease"

      });

      that.animation = animation;

      animation.rotate(0).step(), that.data.runDegs = 0;

 

      that.setData({

        animationData: animation.export(),

        block3: 'none',

        block4: 'block'

      })

 

      for (let x in awardsConfig.awards) {

        awardsConfig.awards[x].opacity = '1';

      }

 

      setTimeout(function () {

        that.setData({

          block1: 'block',

          block4: 'none',

          awardsConfig: awardsConfig,

        })

      }, 300)

    },

 

    //父组件需要切换当前转盘的选项

    //如果有需要切换不同转盘的选项时,可以调用这方法

    //data: 转盘的数据

    //flag: 当转盘在转动过程中如果你想停止的话,可以传个true值,默认可不传

    switchZhuanpan(data, flag) {

      this.setData({

        awardsConfig: data,

        block1: 'block',

        block1: 'none',

        block3: 'none',

        block4: 'none',

        zhuanflg: false,

      })

      this.initAdards();

 

      if (flag) {

        this.reset();

        clearTimeout(this.data.timer);

        start.stop();

        mid.stop();

        stop.stop();

        wx.removeStorageSync('repeatArr');

      }

    },



 

    /*

    * 内部私有方法建议以下划线开头

    * triggerEvent 用于触发事件,过triggerEvent来给父组件传递信息的

    * 写法: this.triggerEvent('cancelEvent', { num: 1 })  // 可以将num通过参数的形式传递给父组件

    */

 

    // GO转盘开始转动

    _zhuan() {

      var that = this, awardsConfig = that.data.awardsConfig, runDegs = that.data.runDegs;

 

      var num0 = that.data.num ;

 

      if(num0 > 0){

        //>>> 是无符号移位运算符

        var r = Math.random() * awardsConfig.awards.length >>> 0, runNum = 8;// 根据尹工的逻辑,这里的r的值从后端获取,不需要我来计算

 

        /*=============不重复抽取=============*/

        if (that.data.repeat) {

          r = that._queryRepeat(r);

        } else {

          wx.removeStorageSync('repeatArr');

 

          console.log('是否开启了概率???', that.data.probability);

          //开启概率 probability这属性必须要传个ture

          if (that.data.probability) {

            r = that._openProbability();

          }

        }

        /*=============不重复抽取=============*/

        console.log('当前答案选项的下标=====', r);

        setTimeout(function () {

          //转盘开始转动音乐

          that.data.musicflg ? that.data.fastJuedin ? mid.play() : start.play() : '';

 

          //要转多少度deg

          runDegs = runDegs || 0, runDegs = runDegs + (360 - runDegs % 360) + (2160 - r * (360 / awardsConfig.awards.length));

 

          var animation = wx.createAnimation({

            duration: that.data.fastJuedin ? that.data.slowTime : that.data.fastTime,

            timingFunction: "ease"

          });

          that.animation = animation;

 

          //这动画执行的是差值 

          //如果第一次写rotate(360) 那么第二次再写rotate(360)将不起效果

          animation.rotate(runDegs).step(), 0 == r && (runDegs = 0);

 

          that.setData({

            animationData: animation.export(),

            block1: 'none',

            block2: 'block',

            zhuanflg: true,

          })

 

          that._setatZhuan(true);

        }, 100);

 

        that.setData({

          timer: setTimeout(function () {

            //转盘停止后,答案区域高亮显示,其他区域增加透明度

            for (let x in awardsConfig.awards) {

              if (x != r) {

                awardsConfig.awards[x].opacity = '0.3';

              } else {

                awardsConfig.awards[x].opacity = '1';

              }

            }

 

            //转盘停止后的音乐

            !that.data.musicflg ? '' : stop.play();

            let num1 = that.data.num;

            that.setData({

              animationData: {},

              s_awards: awardsConfig.awards[r].id,//最终选中的结果

              awardsConfig: awardsConfig,

              block2: 'none',

              block3: 'block',

              zhuanflg: false,

              num: --num1

            })

            that._myAwards();

            that._setatZhuan(false);

          }, that.data.fastJuedin ? that.data.slowTime : that.data.fastTime)

        })

 

      }else{

        wx.showToast({

          title: '您没有抽奖机会',

          icon: '',

          image: '/images/tishi.png',

          duration: 1000

        })

      }

      

    },


 

    // 开启概率 

    // 传的数越大概率越大

    // 传入0的话就永远摇不到这个选项

    _openProbability() {

      var that = this, awards = that.data.awardsConfig.awards, arr = [];

      //5, 5, 20, 10 ,30 ,30, 0

      for (let i in awards) {

        if (awards[i].probability != 0) {

          for (var x = 0; x < awards[i].probability; x++) {

            //把当前的概率数字 以当前选项下标的形式 都添加都空数组中,然后随机这个数组

            arr.push(i);

          }

        }

      }

      var s = Math.floor(Math.random() * arr.length);

      return arr[s];

    },

 

    //不重复抽取

    //r:随机数 当前选项进行随机

    _queryRepeat(r) {

      var that = this, flag = true, repeatArr = wx.getStorageSync('repeatArr'), repeatArr2 = [], awardsConfig = that.data.awardsConfig;

      if (that.isNull(repeatArr)) {

        repeatArr2.push(r), wx.setStorageSync('repeatArr', repeatArr2);

        return r;

      } else {

        var len = awardsConfig.awards.length, r = Math.random() * len >>> 0;

        for (let i in repeatArr) {

          if (r == repeatArr[i]) {

            flag = false;

            if (repeatArr.length == len) {

              wx.removeStorageSync('repeatArr');

              repeatArr2.push(r), wx.setStorageSync('repeatArr', repeatArr2);

              return r;

            } else {

              return that._queryRepeat();//递归调用

            }

          }

        }

        if (flag) {

          repeatArr.push(r), wx.setStorageSync('repeatArr', repeatArr);

          return r;

        }

      }

    },

 

    //初始化数据时向外传的参

    _change() {

      this.triggerEvent('myData', this.data.awardsConfig);// 向父组件传出当前转盘的数组数据

    },

 

    //当前转盘的结果

    _myAwards() {

      this.triggerEvent('myAwards', this.data.s_awards)

    },

 

    //转盘开始转动或者结速转动后的要传的值

    _setatZhuan(e) {

      this.triggerEvent('startZhuan', e);

    },

 

  }

})


以上部分是组件的文件内容

下面来说组件的使用

在使用组件的json文件中设置如下

{

  "navigationBarTitleText": "抽奖",

  "usingComponents": {

    "cj": "../../Component/cj"

  }

}

页面中组件的使用如下

<!-- 自定义组件开始 -->

<cj id="cj" zhuanpanArr="{{zhuanpanArr}}" num="{{num}}" wx:if="{{isShow}}" bind:myAwards="onGetCode"></cj>

在JS文件中设置变量

data: {

    isShow : true,

}

目的是在更改组件中的变量以后刷新页面,所以增加如下判断:

wx.request({

      url: url+'choujiang', 

      data: {

        uid:app.globalData.uid

      },

      header: {

        'content-type': 'application/json' // 默认值

      },

      success (res) {

        console.log(res.data.data);

        that.setData({

          isShow:false

        })

        that.data.zhuanpanArr = res.data.data.zhuanpanArr;

        that.data.num = res.data.data.num;

        that.setData({

          // zhuanpanArr : res.data.data,

          num : res.data.data.num,

          zhuanpanArr: res.data.data.zhuanpanArr,

          jzgz:res.data.data.jzgz,

          pbgz:res.data.data.pbgz

        })

        console.log(that.data.zhuanpanArr);

        that.setData({

          isShow:true

        })

      }

    })

这样就可以实现自定义组件中的值与页面数据的交互了。