Vue+CSS3实现转盘抽奖的示例代码

所属分类: 网页制作 / CSS 阅读数: 1050
收藏 0 赞 0 分享

最近有个转盘抽奖的需求,搜了一下现有的轮子,有的是用jQuery的动画函数实现的,有的是用canvas绘图然后再用高频率的setTimeout调用旋转方法,前者太老了没法简单移植到vue项目,后者感觉性能表现可能不会太好。也有一些用CSS动画的方案,设计了加速-匀速-减速三个动画,再计算偏转角度让三个动画尽可能无缝衔接,但我感觉绕了大远路,应该有更简单轻量的实现方案。个人更倾向于用transition来实现,不过网上的例子感觉还不够好,有的倾斜文字都没有对齐,最后还是自己手写了一个。

无需jQuery,不用js脚本实现动画细节,动画效果完全连续无需衔接,支持动态设置2个以上任意数量的奖项。

核心思路是用transition以及rotate实现旋转动画,使用transition-origin和rotate绘制出定位较为精确的轮盘奖项。

代码及实例展示

<script src="//unpkg.com/vue/dist/vue.js"></script>
<span id="app">
  <span>Prize number: {{ prizeNumber }}</span>
  <button type="button" @click="!rolling && prizeNumber < 8 && (prizeNumber++)" :disabled="rolling || prizeNumber === 8">Add</button>
  <button type="button" @click="!rolling && prizeNumber > 2 && (prizeNumber--)" :disabled="rolling || prizeNumber === 2">Remove</button>
   <div class="wheel-wrapper">
    <div
      class="wheel-pointer"
      @click="onClickRotate"
    >
      Start
    </div>
    <div
      class="wheel-bg"
      :class="{freeze: freeze}"
      :style="`transform: rotate(${wheelDeg}deg)`"
    >
      <div class="prize-list">
        <div
          class="prize-item-wrapper"
          v-for="(item,index) in prizeList"
          :key="index"
        >
          <div
            class="prize-item"
            :style="`transform: rotate(${(360/ prizeList.length) * index}deg)`"
          >
            <div class="prize-name">
              {{ item.name }}
            </div>
            <div class="prize-icon">
              <img :src="item.icon">
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
html {
   background: #DD7C7D;
}

.wheel-wrapper {
    width: 300px;
    height: 300px;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }

  .wheel-pointer {
    width: 60px;
    height: 60px;
    border-radius: 1000px;
    background: yellow;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    line-height: 60px;
    z-index: 10;
  }
  .wheel-bg {
    width: 100%;
    height: 100%;
    border-radius: 1000px;
    overflow: hidden;
    transition: transform 4s ease-in-out;
    background: #7EEF97;
    
    &.freeze {
      transition: none;
      background: red;
    }
  }

  .prize-list {
    width: 100%;
    height: 100%;
    position: relative;
    text-align: center;
  }

  .prize-item-wrapper {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 150px;
    height: 150px;
  }

  .prize-item {
    width: 100%;
    height: 100%;
    transform-origin: bottom;

    .prize-name {
      padding: 16px 0;
    }

    .prize-icon {}
  }

var Main = {
  data() {
    return {
      freeze: false,
      rolling: false,
      wheelDeg: 0,
      prizeNumber: 8,
      prizeListOrigin: [
        {
          icon: "https://picsum.photos/40?random=1",
          name: "$10000"
        },
        {
          icon: "https://picsum.photos/40?random=6",
          name: "Thank you!"
        },
                {
          icon: "https://picsum.photos/40?random=2",
          name: "$500"
        },
        {
          icon: "https://picsum.photos/40?random=3",
          name: "$100"
        },
                {
          icon: "https://picsum.photos/40?random=6",
          name: "Thank you!"
        },
        {
          icon: "https://picsum.photos/40?random=4",
          name: "$50"
        },
        {
          icon: "https://picsum.photos/40?random=5",
          name: "$10"
        },
        {
          icon: "https://picsum.photos/40?random=6",
          name: "Thank you!"
        }
      ]
    };
  },
  computed: {
    prizeList () {
      return this.prizeListOrigin.slice(0, this.prizeNumber)
    }
  },
  methods: {
    onClickRotate() {
      if (this.rolling) {
        return;
      }
      this.rolling = true;
      const { wheelDeg, prizeList } = this;
      const random = Math.floor(Math.random() * (prizeList.length ));
      console.log(random);
      this.wheelDeg =
        wheelDeg -
        wheelDeg % 360 +
        6 * 360 +
        (360 - 360 / prizeList.length * random);
      setTimeout(() => {
        this.rolling = false;
        alert("Result:" + prizeList[random].name);
      }, 4500);
    }
  },
  watch: {
    prizeNumber () {
      this.freeze = true
      this.wheelDeg = 0
     
      setTimeout(() => {
         this.freeze = false
      }, 0)
    }
  }
};
var App = Vue.extend(Main);
new App().$mount("#app");

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

css实现的让图片垂直居中的方法

图片的宽度和高度是未知的,没有一个固定的尺寸,在这个前提下要使图片在一个固定了宽度和高度的容器中垂直居中,想想感觉还是挺麻烦的,由于最近的项目可能会用到这个方案,所以把一些常用的方法都收集整理了一下。
收藏 0 赞 0 分享

3个比较有用的网页制作技巧

我们在进行DivCSS布局的时候,非常关注CSS技巧的学习,今天向大家推荐3个所谓的 “顶级”CSS技巧!虽然没有什么新意,但对新手而言却非常重要!
收藏 0 赞 0 分享

让你写出更轻巧、更快并且更不会让你头疼的CSS的方法

为什么我们的CSS变得一团糟——我们真的很容易陷入这样的困惑中。有时这是一开始就马虎编程的结果,有时是由于后期多重的附加代码(hacks)和修改造成的。
收藏 0 赞 0 分享

利用CSS3的checked伪类实现OL的隐藏显示的方法

随着CSS3的发布,国外研究正如火如荼,但在国内还有很多人抱着IE不支持CSS3的想法,始终无动于衷不肯去学习。
收藏 0 赞 0 分享

CSS多浏览器兼容性(IE和Firefox)技巧大全推荐

CSS对浏览器的兼容性有时让人很头疼,或许当你了解当中的技巧跟原理,就会觉得也不是难事,从网上收集了IE7,6与Fireofx的兼容性处理技巧并整理了一下。
收藏 0 赞 0 分享

固定位置显示弹出层(兼容IE6,IE8,FF)

兼容IE6,IE8,FF弹出层固定位置显示,主要是通过css的控制
收藏 0 赞 0 分享

div完美自适应动态上下左右居中

div完美自适应动态上下左右居中,多用于信息提示框效果。
收藏 0 赞 0 分享

display:inline的用法

和 display:inline 对应的是 display:block,block 会让应用了该 CSS 属性的 HTML 标记变成块级别元素,例如 SPAN 是行内显示的,但是你加了 display:block 属性就不一样了
收藏 0 赞 0 分享

css样式实现整个页面背景使用一张图片

最近一直被css背景困扰,因为是仿站,别人网站背景图片无论怎样另存,都是相同文件名,打开一看,整个网站的各个角落背景图片都在一个文件中分布,于是上网搜集资料,发现使用这种方法的网站很多,源自于雅虎最早。
收藏 0 赞 0 分享

关于CSS样式表文件组织形式的整理

以下引用网络上的一篇文章,不知道作者是谁了,总体上概括的不错,但是在实际应用当中还是要根据项目情况来具体实施比较好。
收藏 0 赞 0 分享
查看更多