uniapp的动画运用(一)旋转动画《抽奖大转盘》

今天简单记录一下如何使用uni.createAnimation(OBJECT)来构建一个抽奖大转盘。效果如下图所示。

效果

一、大转盘布局分解

1.整体圆盘分为5层,从外到内的顺序依次是:

  • 1.黄色层 > 2.红色层 > 3.白色层 > 4.粉色层 > 5.红色层(启动按钮)。


    层分解

二、霓虹灯排布

  1. 小灯的排布是基于父级(第2层)进行绝对定位的。
  2. 小灯是按照一定角度围绕中心点旋转排布的。
  3. 小灯是闪烁的,可以使用定时器构建出交替闪烁的效果。

1.模板代码

<!-- 圆盘周围的圆点 -->
          <view
            class="dot-wrap"
            v-for="(degree, index) in dotList"
            :key="getItemKey('dot-', index)"
            :style="{ transform: 'rotate(' + degree + ')' }"
          >
            <view
              class="dot"
              :class="{
                'dot-light':
                  (lightMode == 1 && index % 2 != 0) ||
                  (lightMode == 2 && index % 2 == 0)
              }"
            ></view>
          </view>
          <!-- 圆盘周围的圆点 -->

2.使用计算属性构建点列表

 computed: {
    //圆点列表
    dotList() {
      let dList = []
      let dotNum = 20
      let rotateDeg = (1 / dotNum) * 360 // 每个灯旋转的角度
      for (let index = 0; index < dotNum; index++) {
        dList.push(index * rotateDeg + 'deg')
      }
      return dList
    },
   ...
}

3.开启定时器

 data() {
    return {
      //亮灯模式
      lightMode: 1,
      ...
    }
  },
created() {
    //一进来开启跑马灯
    setInterval(() => {
      this.lightMode = this.lightMode === 1 ? 2 : 1
    }, 500)
  },

4.小灯的样式

.play-circle-a {
        width: 100%;
        height: 100%;
        border-radius: 50%;
        border: 4rpx solid #dc3a1f;
        background-color: #ff7a01;
        display: flex;
        justify-content: center;
        align-items: center;
        position: relative;
        .dot-wrap {
          position: absolute;
          width: 100%;
          display: flex;
          align-items: center;
          z-index: 998;
          padding-left: 10rpx;
          .dot {
            width: 12rpx;
            height: 12rpx;
            background: #edc225;
            box-shadow: 0rpx 0rpx 12rpx 0rpx #ffdb2c;
            border-radius: 50%;
            //设置好旋转中心点
            transform-origin: 50% 330rpx;
          }
 }

//圆点亮灯的颜色
.dot-light {
  background-color: #fef0ab !important;
}

三、奖品区域分隔线绘制

  1. 分割线实际是一个宽度很小的矩形。
  2. 线条的个数实际是等于奖品的数量的。
  3. 线条是基于父级(第3层)进行绝对定位,并且按照父级的中心点旋转排布的。

1. 模板代码

 <!-- 渲染分割线区域 -->
            <view
              class="sep-line-item"
              :class="{
                'sep-line-color-pink': styleThemeType === 'pink',
                'sep-line-color-purple': styleThemeType === 'purple'
              }"
              v-for="(item, index) in prizeList"
              :key="getItemKey('sep-', index)"
              :style="{ transform: 'rotate(' + item.lineTurn + ')' }"
            ></view>
<!-- 渲染分割线区域 -->

2.构建好奖品列表

 computed: {
//整合奖品列表
    prizeList() {
       let pList = []
      中间略
      ...
      //计算出旋转角度
      let turnNum = (1 / pList.length) * 360
      pList.map((item, index) => {
        returnList.push({
          ...item,
          //奖品区域需要旋转的角度
          turn: index * turnNum + 'deg',
          //分隔线需要旋转的角度
          lineTurn: index * turnNum + turnNum / 2 + 'deg'
        })
      })
      return returnList
    },
   ...
}

3. css代码

.play-circle-inner {
          position: relative;
          width: 550rpx;
          height: 550rpx;
          border-radius: 50%;
          border: 6rpx solid #dc381f;
          background-color: #ffffff;
          //分隔线
          .sep-line-item {
            position: absolute;
            top: -4rpx;
            left: 269rpx;
            width: 3rpx;
            height: 275rpx;
            overflow: hidden;
            background-color: #dc381f;
            //设置好旋转中心点
            transform-origin: 50% 275rpx;
          }
      ....
}

四、奖品区域排布

  1. 奖品区域实际是有一个个的矩形(如图中紫色线条的矩形)。
  2. 奖品区域是基于父级(第3层)进行绝对定位,并且按照父级的中心点旋转排布的。

1.模板代码

<!-- 奖品显示区域 -->
            <view
              class="prize-item"
              v-for="(item, index) in prizeList"
              :key="getItemKey('prize-', index)"
              :style="{ transform: 'rotate(' + item.turn + ')' }"
            >
              <text
                class="prize-title ellipsis"
                :class="{
                  'w-60': prizeList.length > 7 && prizeList.length <= 11,
                  'w-50': prizeList.length > 11,
                  'prize-title-color-pink': styleThemeType === 'pink',
                  'prize-title-color-purple': styleThemeType === 'purple'
                }"
                >{{ item.title }}</text
              >
              <image
                v-if="item.indexpic"
                :src="item.indexpic"
                :lazy-load="true"
                class="prize-img"
                :class="{
                  round: prizeList.length > 7,
                  'small-image': prizeList.length > 9
                }"
              ></image>
            </view>
<!-- 奖品显示区域 -->

2.css代码

.play-circle-inner {
          position: relative;
          width: 550rpx;
          height: 550rpx;
          border-radius: 50%;
          border: 6rpx solid #dc381f;
          background-color: #ffffff;
          //奖品
          .prize-item {
            width: 100%;
            position: absolute;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding-top: 30rpx;
            transform-origin: 50% 275rpx;
            .prize-title {
              max-width: 190rpx;
              color: #a34033;
              margin-bottom: 24rpx;
              text-align: center;
            }
            .prize-img {
              width: 76rpx;
              height: 76rpx;
            }
          }
 }

五、转盘动画

  1. 点击中间按钮之后去服务端请求抽奖接口(可以额外做一些判断,次数等等进行防刷)。
  2. 然后根据服务端返回的抽奖结果定位到中奖所在的奖品列表中的索引。
  3. 根据中奖奖品所在的索引计算出转盘最后需要转的角度,以确保最终小箭头能够刚好指向所中的奖品,然后开始启动转盘动画。
  4. 转盘动画旋转过程中需要设置正在旋转的状态,防止连续重复点击,当转盘停止之后重置状态,就可以再一次启动转盘了。

1.旋转动画

//旋转圆盘
    rotate() {
      let runNum = 8 //旋转圈数
      let duration = 4000 //旋转的时长
      // 根据中奖的索引值计算出旋转角度
      this.rotateDeg = this.rotateDeg || 0
      //每次旋转的角度需要累加上之前已经旋转过的角度,不然的话会出现只能旋转一次
      this.rotateDeg =
        this.rotateDeg +
        (360 - (this.rotateDeg % 360)) +
        (360 * runNum - this.winPrizeIndex * (360 / this.prizeList.length))
      //创建动画
      let animationRun = uni.createAnimation({
        duration: duration,
        timingFunction: 'ease'
      })
      animationRun.rotate(this.rotateDeg).step()
      //导出动画(开启动画)
      this.animationData = animationRun.export()
      this.rotateStatus = true
      setTimeout(() => {
        //停止旋转重置状态
        this.stopRotate()
      }, 4100)
    },

六、整体代码逻辑(做了简化)

<template>
  <!-- 大转盘组件 -->
  <view class="big-wheel-wrap">
    <!-- 大转盘背景区域 -->
    <view class="big-wheel-bg-wrap">
      <!-- 旋转区域 -->
      <view class="big-wheel-play-wrap">
        <view class="play-circle-a">
          <!-- 圆盘周围的圆点 -->
          <view
            class="dot-wrap"
            v-for="(degree, index) in dotList"
            :key="getItemKey('dot-', index)"
            :style="{ transform: 'rotate(' + degree + ')' }"
          >
            <view
              class="dot"
              :class="{
                'dot-light':
                  (lightMode == 1 && index % 2 != 0) ||
                  (lightMode == 2 && index % 2 == 0)
              }"
            ></view>
          </view>
          <!-- 圆盘周围的圆点 -->

          <!-- 最里面的圆盘 -->
          <view class="play-circle-inner" :animation="animationData">
            <!-- 渲染分割线区域 -->
            <view
              class="sep-line-item"
              v-for="(item, index) in prizeList"
              :key="getItemKey('sep-', index)"
              :style="{ transform: 'rotate(' + item.lineTurn + ')' }"
            ></view>
            <!-- 渲染分割线区域 -->

            <!-- 奖品显示区域 -->
            <view
              class="prize-item"
              v-for="(item, index) in prizeList"
              :key="getItemKey('prize-', index)"
              :style="{ transform: 'rotate(' + item.turn + ')' }"
            >
              <text
                class="prize-title ellipsis"
                :class="{
                  'w-60': prizeList.length > 7 && prizeList.length <= 11,
                  'w-50': prizeList.length > 11
                }"
                >{{ item.title }}</text
              >
              <image
                v-if="item.indexpic"
                :src="item.indexpic"
                :lazy-load="true"
                class="prize-img"
                :class="{
                  round: prizeList.length > 7,
                  'small-image': prizeList.length > 9
                }"
              ></image>
            </view>
            <!-- 奖品显示区域 -->
          </view>
          <!-- 最里面的圆盘 -->

          <!-- 启动抽奖按钮 -->
          <view class="start-btn-wrap">
            <!-- 指针 -->
            <view class="indicator"></view>
            <!-- 指针 -->
            <!-- 圆环按钮 -->
            <view class="circle-btn-outer" @click="submitLottery()">
              <view
                class="circle-btn-inner"
                :class="{
                  'forbid-bg': userLotteryTimes === 0
                }"
              >
                <text class="btn-text">启动抽奖</text>
              </view>
            </view>
            <!-- 圆环按钮 -->
          </view>
          <!-- 启动抽奖按钮 -->
        </view>
      </view>
      <!-- 旋转区域 -->
      <!-- 支架 -->
      <image :src="resource.supportImageUrl" class="support-image"></image>
      <!-- 支架 -->
    </view>
    <!-- 大转盘背景区域 -->

    <!-- 弹窗提示组件 -->
    <lottery-toast ref="lotteryToast" :user-lottery-times="userLotteryTimes" />
    <!-- 弹窗提示组件 -->

    <!-- 提示 -->
    <u-toast ref="uToast" />
    <!-- 提示 -->
  </view>
</template>
<script>
//导入公共截图方法
import { getImageUrl } from '@/common/services/utils'
//导入弹窗提示组件
import lotteryToast from '../modal/lottery-toast'

export default {
  components: {
    lotteryToast
  },
  props: {
    //用户剩余的抽奖次数
    userLotteryTimes: {
      type: Number,
      default: -1
    },
    //用户传递的奖品列表
    userPrizeList: {
      type: Array,
      default() {
        return []
      }
    },
    //活动信息
    activity: {
      type: Object,
      default() {
        return {}
      }
    },
    //提交
    submit: {
      type: Function,
      default() {
        return () => {}
      }
    }
  },
  computed: {
    //谢谢参与数据结构
    thanksObj() {
      return {
        id: -1,
        type: -1,
        indexpic: this.resource.thanksIconUrl,
        title: '谢谢参与'
      }
    },
    //圆点列表
    dotList() {
      let dList = []
      let dotNum = 20
      let rotateDeg = (1 / dotNum) * 360 // 每个灯旋转的角度
      for (let index = 0; index < dotNum; index++) {
        dList.push(index * rotateDeg + 'deg')
      }
      return dList
    },
    //整合奖品列表
    prizeList() {
      let pList = []
      if (this.userPrizeList.length) {
        //首先打乱原始数组
        let oriPrizeList = this.$u.randomArray(this.userPrizeList)
        oriPrizeList.map((item, index) => {
          if (index < 11) {
            let pTitle = item.title
            pList.push({
              //奖项ID
              id: item.id,
              //奖项类型
              type: item.type,
              //奖项图片
              indexpic: this.getPrizeIndexPic(item),
              //选项标题
              title: pTitle
            })
          }
        })
        //如果不足6个奖品则要补足
        if (pList.length < 6) {
          let addNum = 6 - pList.length
          let num = 0
          while (num < addNum) {
            pList.push(this.thanksObj)
            num++
          }
        } else {
          //补一个谢谢参与
          pList.push(this.thanksObj)
        }
      } else {
        let num = 0
        while (num < 6) {
          pList.push(this.thanksObj)
          num++
        }
      }
      //打乱数组
      pList = this.$u.randomArray(pList)
      //打散之后
      let returnList = []
      //计算出旋转角度
      let turnNum = (1 / pList.length) * 360
      pList.map((item, index) => {
        returnList.push({
          ...item,
          //图片&文字需要旋转的角度
          turn: index * turnNum + 'deg',
          //分隔线需要旋转的角度
          lineTurn: index * turnNum + turnNum / 2 + 'deg'
        })
      })
      return returnList
    }
  },
  data() {
    return {
      //图标资源
      resource: {
        //优惠券图标
        couponIconUrl:
          this.$cnf.resDomains.image +
          '/1/20102/2021/1022/617278d8f23e0vyf.png',
        //积分图标
        integralIconUrl:
          this.$cnf.resDomains.image +
          '/1/20102/2022/0126/61f0ee4a19101qwt.png',
        //金币图标
        goldIconUrl:
          this.$cnf.resDomains.image +
          '/1/20102/2021/1014/6167d43ece520oef.png',
        //谢谢参与图片URL
        thanksIconUrl:
          this.$cnf.resDomains.image +
          '/1/20102/2021/0823/61236a0254a5438c.png',
        //支架图片
        supportImageUrl:
          this.$cnf.resDomains.image + '/1/20102/2022/0204/61fd33f839b2ccc5.png'
      },
      //亮灯模式
      lightMode: 1,
      //动画效果参数
      animationData: {},
      //旋转角度
      rotateDeg: 0,
      //是否旋转
      rotateStatus: false,
      //中奖索引
      winPrizeIndex: ''
    }
  },
  created() {
    //一进来开启跑马灯
    setInterval(() => {
      this.lightMode = this.lightMode === 1 ? 2 : 1
    }, 500)
  },
  methods: {
    //获取奖项索引图
    getPrizeIndexPic(item) {
      let indexPic = ''
      //设置中奖奖品图片
      if (item.type === 1) {
        //商品类型
        indexPic = item.goods_indexpic
          ? getImageUrl(JSON.parse(item.goods_indexpic, 100, 100))
          : ''
      } else if (item.type === 2) {
        //优惠券类型
        indexPic = this.resource.couponIconUrl
      } else if (item.type === 4) {
        //积分类型
        indexPic = this.resource.integralIconUrl
      } else if (item.type === 5) {
        //金币类型
        indexPic = this.resource.goldIconUrl
      }
      return indexPic
    },
    //获取key
    getItemKey(key, index) {
      return key + index
    },
    //提交抽奖
    async submitLottery() {
      //正在旋转中不可点击
      if (this.rotateStatus) {
        return
      }
      //判断活动在不在时间范围之内
      if (this.activity.start_state === 2) {
        this.$refs.uToast.show({
          title: '活动未开始',
          type: 'error'
        })
        return
      } else if (this.activity.start_state === 3) {
        this.$refs.uToast.show({
          title: '活动已结束',
          type: 'error'
        })
        return
      }
      //抽之前判断用户的抽奖次数有没有用完
      if (this.userLotteryTimes === 0) {
        return
      }
      //标记已经开始旋转
      this.rotateStatus = true
      //每次点击之前通知父组件
      this.$emit('submit')
      //去服务端请求抽奖接口
      const res = await this.submit(this.activity)
      if (res.code === '0') {
        //说明中奖了,设置中到的奖品的索引位置
        this.setWinPrizeIndex(res.data.id)
        //启动转圈
        this.rotate()
      } else {
        //设置为谢谢参与
        this.setWinPrizeIndex(-1)
        if (res.code !== '1001') {
          //启动转圈
          this.rotate()
        } else {
          //重置状态
          this.rotateStatus = false
        }
      }
    },
    //旋转圆盘
    rotate() {
      let runNum = 8 //旋转圈数
      let duration = 4000 //旋转的时长
      // 旋转角度
      this.rotateDeg = this.rotateDeg || 0
      this.rotateDeg =
        this.rotateDeg +
        (360 - (this.rotateDeg % 360)) +
        (360 * runNum - this.winPrizeIndex * (360 / this.prizeList.length))
      //创建动画
      let animationRun = uni.createAnimation({
        duration: duration,
        timingFunction: 'ease'
      })
      animationRun.rotate(this.rotateDeg).step()
      this.animationData = animationRun.export()
      this.rotateStatus = true
      setTimeout(() => {
        this.stopRotate()
      }, 4100)
    },
    //设置抽中的奖品所在奖品列表的索引
    setWinPrizeIndex(prizeId) {
      this.prizeList.some((item, index) => {
        if (item.id === prizeId) {
          this.winPrizeIndex = index
          return true
        }
      })
    },
    //停止旋转
    stopRotate() {
      this.rotateStatus = false
      //获取配置的提示信息
      const resultTip = this.activity.rule.page_result
      //停止了就给出提示信息
      if (this.winPrizeIndex || this.winPrizeIndex === 0) {
        let prizeInfo = this.prizeList[this.winPrizeIndex]
        if (prizeInfo.type !== -1) {
          setTimeout(() => {
            this.$refs.lotteryToast.show({
              type: 'success',
              title: resultTip.win.tip_title
                ? resultTip.win.tip_title
                : '中奖啦',
              brief: resultTip.win.tip_text
                ? resultTip.win.tip_text
                : '恭喜您获得',
              prizeTitle: prizeInfo.title,
              image: prizeInfo.indexpic
            })
            this.rotateStatus = false
          }, 500)
        } else {
          setTimeout(() => {
            //谢谢参与
            this.$refs.lotteryToast.show({
              type: 'error',
              title: resultTip.no_win.tip_title
                ? resultTip.no_win.tip_title
                : '未中奖',
              brief: resultTip.no_win.tip_text
                ? resultTip.no_win.tip_text
                : '很遗憾,再接再厉啦'
            })
            this.rotateStatus = false
          }, 500)
        }
      } else {
        setTimeout(() => {
          //谢谢参与
          this.$refs.lotteryToast.show({
            type: 'error',
            title: resultTip.no_win.tip_title
              ? resultTip.no_win.tip_title
              : '未中奖',
            brief: resultTip.no_win.tip_text
              ? resultTip.no_win.tip_text
              : '很遗憾,再接再厉啦'
          })
        }, 500)
        this.rotateStatus = false
      }
    }
  }
}
</script>

<style lang="scss">
.big-wheel-wrap {
  .big-wheel-bg-wrap {
    height: 724rpx;
    position: relative;
    display: flex;
    justify-content: center;
    .support-image {
      width: 430rpx;
      height: 100%;
      position: absolute;
      top: 0rpx;
      z-index: 1;
    }
    .big-wheel-play-wrap {
      width: 660rpx;
      height: 660rpx;
      box-shadow: 0rpx 0rpx 12rpx 0rpx #ffb901;
      border: 18rpx solid #ffb901;
      border-radius: 50%;
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 900;
      .play-circle-a {
        width: 100%;
        height: 100%;
        border-radius: 50%;
        border: 4rpx solid #dc3a1f;
        background-color: #ff7a01;
        display: flex;
        justify-content: center;
        align-items: center;
        position: relative;
        .dot-wrap {
          position: absolute;
          width: 100%;
          display: flex;
          align-items: center;
          z-index: 998;
          padding-left: 10rpx;
          .dot {
            width: 12rpx;
            height: 12rpx;
            background: #edc225;
            box-shadow: 0rpx 0rpx 12rpx 0rpx #ffdb2c;
            border-radius: 50%;
            transform-origin: 50% 330rpx;
          }
        }
        .play-circle-inner {
          position: relative;
          width: 550rpx;
          height: 550rpx;
          border-radius: 50%;
          border: 6rpx solid #dc381f;
          background-color: #ffffff;
          //分隔线
          .sep-line-item {
            position: absolute;
            top: -4rpx;
            left: 269rpx;
            width: 3rpx;
            height: 275rpx;
            overflow: hidden;
            background-color: #dc381f;
            transform-origin: 50% 275rpx;
          }
          //奖品
          .prize-item {
            width: 100%;
            position: absolute;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding-top: 30rpx;
            transform-origin: 50% 275rpx;
            .prize-title {
              max-width: 190rpx;
              color: #a34033;
              margin-bottom: 24rpx;
              text-align: center;
            }
            .prize-img {
              width: 76rpx;
              height: 76rpx;
            }
          }
        }
        //中奖抽奖按钮
        .start-btn-wrap {
          position: absolute;
          left: 0rpx;
          top: 0rpx;
          width: 100%;
          height: 100%;
          display: flex;
          justify-content: center;
          align-items: center;
          z-index: 999;
          .indicator {
            position: absolute;
            top: 208rpx;
            width: 0;
            height: 0;
            border-left: 14rpx solid transparent;
            border-right: 14rpx solid transparent;
            border-bottom: 40rpx solid #fbc765;
          }
          .circle-btn-outer {
            width: 145rpx;
            height: 145rpx;
            border-radius: 50%;
            background-color: #fbc765;
            display: flex;
            justify-content: center;
            align-items: center;
            .circle-btn-inner {
              width: 116rpx;
              height: 116rpx;
              background-color: #ff7a01;
              border-radius: 50%;
              display: flex;
              justify-content: center;
              align-items: center;
              .btn-text {
                width: 80rpx;
                color: #ffffff;
                font-size: 30rpx;
                font-weight: 500;
                text-align: center;
                line-height: 40rpx;
              }
            }
          }
        }
      }
    }
  }
}

//圆点亮灯的颜色
.dot-light {
  background-color: #fef0ab !important;
}

.small-image {
  width: 60rpx !important;
  height: 60rpx !important;
}

.forbid-bg {
  background-color: #b1b2b3 !important;
}
</style>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • github: https://github.com/LoveIsReal/Lottery 需求: 奖品数量、文字...
    DPBBC阅读 4,157评论 0 11
  • 大转盘啊,大转盘。看了好多代码,很多都是通过canvas实现的,也有别的是通过计时器实现的,但是无非js代码多的一...
    誰在花里胡哨阅读 3,356评论 0 12
  • 今天的任务是做一个纯CSS3的还算比较漂亮的抽奖大转盘,也就是下图效果。 我只说思路和重要的CSS3代码。 外盘 ...
    microkof阅读 4,967评论 7 5
  • 前段时间公司里为了推广微信公众号吸粉,需要在公众号菜单里增加一个大转盘抽奖的活动,在活动现场手机上抽到什么奖品就是...
    IT小C阅读 18,853评论 1 8
  • 有个朋友需要写个抽奖大转盘的功能,就让我帮忙写了下。我用了2种方法实现了效果,在这里和大家一起分享下。 一、一键转...
    干不动阅读 15,061评论 10 41