my-turntable-draw.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <template>
  2. <view class="raffle-wheel" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px'}">
  3. <view class="raffle-wheel-box" :style="{ width: (canvasWidth - 50) + 'px', height: (canvasHeight - 50) + 'px'}">
  4. <view class="raffle-wheel-show"
  5. :style="'transform: rotate(' + rotates + 'deg);transition-duration: ' + duration + 's;'">
  6. <view class="item-block" v-for="(item, index) in prizeList" :key="index">
  7. <view class="item-view-block" :style="
  8. 'transform: rotate(' + (degs * index) + 'deg) skewY(' + skew + 'deg);background: ' + ((index % 2) > 0 ? color1 : color2) + ';'
  9. ">
  10. </view>
  11. <view class="item-view"
  12. :style="'transform: translateX(-50%) rotate(' + (degs * index + (degs / 2)) + 'deg);width: ' + textWidth + ';'">
  13. <text>{{item.prizeName}}</text>
  14. <image :src="item.imageUrl" mode="aspectFill"></image>
  15. </view>
  16. </view>
  17. </view>
  18. <view class="raffle-wheel__action" @click="handleAction"></view>
  19. </view>
  20. </view>
  21. </template>
  22. <script>
  23. export default {
  24. props: {
  25. canvasWidth: {
  26. type: Number,
  27. default: 240
  28. },
  29. canvasHeight: {
  30. type: Number,
  31. default: 240
  32. },
  33. // 奖品列表
  34. prizeList: {
  35. type: Array,
  36. // 必须是偶数 且大于等于4
  37. validator: function(value) {
  38. return value.length % 2 === 0
  39. },
  40. required: true
  41. },
  42. // 奖品区块对应背景颜色
  43. colors: {
  44. type: Array,
  45. default: () => [
  46. '#FFF',
  47. '#FFE9AA'
  48. ],
  49. // 必须是偶数且仅为 2 个颜色相互交替
  50. validator: function(value) {
  51. return value.length === 2
  52. }
  53. },
  54. // 旋转动画时间 单位s
  55. duration: {
  56. type: Number,
  57. default: 8
  58. },
  59. // 旋转的圈数
  60. ringCount: {
  61. type: Number,
  62. default: 8
  63. },
  64. // 奖品名称所对应的 key 值
  65. strKey: {
  66. type: String,
  67. default: String
  68. },
  69. // 中奖单位
  70. targetIndex: {
  71. type: Number,
  72. default: 0
  73. }
  74. },
  75. data() {
  76. return {
  77. isClick: true, // 当前是否可点击
  78. isShow: true, // 是否初始化
  79. skew: 0, // 偏移角度
  80. degs: 0, // 旋转角度
  81. color1: '#FFF',
  82. color2: '#FFF',
  83. textWidth: '50%',
  84. rotates: 0, // box旋转角度
  85. isRoteIndex: 0 // 已经选中的key
  86. }
  87. },
  88. mounted() {
  89. this.color1 = this.colors[0]
  90. this.color2 = this.colors[1]
  91. this.setView()
  92. },
  93. methods: {
  94. // 计算当前每个deg
  95. setView() {
  96. let length = this.prizeList.length
  97. this.textWidth = 180 / length + '%'
  98. let _degs = 360 / length // 每个距离上一个的偏移角度
  99. if (length < 4) {
  100. } else { // 奖项大于等于4个
  101. this.degs = _degs
  102. this.skew = _degs - 90
  103. }
  104. },
  105. // 点击抽奖
  106. handleAction() {
  107. if (!this.isClick) {
  108. return false
  109. }
  110. this.isClick = false
  111. this.$emit('befoterClick', {
  112. type: 'start',
  113. callback: this.rotoreAction
  114. })
  115. },
  116. rotoreAction(index) {
  117. if (index === -1) {
  118. this.isClick = true;
  119. return
  120. }
  121. let _rotates = 0
  122. if (this.isShow) {
  123. let _len = this.prizeList.length - 1
  124. // 第一次角度 => (总项 - 当前项) * 角度 + 360 * 圈数
  125. _rotates = (_len - index) * this.degs + 360 * this.ringCount
  126. this.isShow = false
  127. this.rotates += _rotates + (this.degs / 2)
  128. } else {
  129. // 不是第一次 =>
  130. _rotates = -(index - this.isRoteIndex) * this.degs + 360 * this.ringCount
  131. this.rotates += _rotates
  132. }
  133. this.isRoteIndex = index
  134. this.setTimeOut()
  135. },
  136. setTimeOut() {
  137. setTimeout(() => {
  138. console.log('转动结束')
  139. this.$emit('afterClick', {
  140. type: 'end',
  141. callback: this.endAction
  142. })
  143. this.isClick = true
  144. }, this.duration * 1000 + 100)
  145. },
  146. endAction() {
  147. // console.log('本次选中的:' + this.prizeList[this.targetIndex].name)
  148. }
  149. }
  150. }
  151. </script>
  152. <style lang="scss">
  153. // $actionBgUrl: '~static/raffle-wheel/raffle-wheel__action';
  154. // $raffleBgUrl: '~static/raffle-wheel/raffle-wheel__bg';
  155. .raffle-wheel {
  156. position: relative;
  157. margin: 0 auto;
  158. display: flex;
  159. justify-content: center;
  160. align-items: center;
  161. background-repeat: no-repeat;
  162. background-position: center center;
  163. background-size: contain;
  164. background-image: url("@/static/Lottery/circle.png");
  165. // background-image: url($raffleBgUrl + ".png");
  166. // @media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2) {
  167. // background-image: url($raffleBgUrl + "@2x.png");
  168. // }
  169. // @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3) {
  170. // background-image: url($raffleBgUrl + "@3x.png");
  171. // }
  172. }
  173. .raffle-wheel-box {
  174. border-radius: 50%;
  175. overflow: hidden;
  176. background-color: #f2f2f2;
  177. box-shadow: 1px 1px 1px 1px #FFE9AA;
  178. position: relative;
  179. }
  180. .raffle-wheel-show {
  181. width: 100%;
  182. height: 100%;
  183. position: relative;
  184. }
  185. .item-block {
  186. width: 100%;
  187. height: 100%;
  188. position: absolute;
  189. }
  190. .item-view-block {
  191. position: absolute;
  192. top: 0;
  193. right: 0;
  194. width: 50%;
  195. height: 50%;
  196. transform-origin: 0% 100%;
  197. }
  198. .item-view {
  199. box-sizing: border-box;
  200. position: absolute;
  201. top: 0;
  202. left: 50%;
  203. width: 25%;
  204. height: 50%;
  205. transform-origin: center 100%;
  206. font-size: 26upx;
  207. text-align: center;
  208. padding-top: 20upx;
  209. text {
  210. display: -webkit-box;
  211. -webkit-box-orient: vertical;
  212. -webkit-line-clamp: 2;
  213. overflow: hidden;
  214. width: 100%;
  215. line-height: 30upx;
  216. }
  217. image {
  218. display: block;
  219. width: 100rpx;
  220. height: 100rpx;
  221. margin: 10upx auto 0;
  222. }
  223. }
  224. .raffle-wheel__action {
  225. position: absolute;
  226. top: calc(50% - 58px);
  227. left: calc(50% - 58px);
  228. width: 114px;
  229. height: 114px;
  230. background-repeat: no-repeat;
  231. background-position: center center;
  232. background-size: contain;
  233. background-image: url("@/static/Lottery/pointer.png");
  234. // background-image: url($actionBgUrl + ".png");
  235. // @media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2) {
  236. // background-image: url($actionBgUrl + "@2x.png");
  237. // }
  238. // @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3) {
  239. // background-image: url($actionBgUrl + "@3x.png");
  240. // }
  241. }
  242. </style>