lucky-grid.vue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <template>
  2. <view v-if="isShow" class="lucky-box" :style="{ width: boxWidth + 'px', height: boxHeight + 'px' }">
  3. <canvas
  4. type="2d"
  5. id="lucky-grid"
  6. canvas-id="lucky-grid"
  7. :style="{ width: boxWidth + 'px', height: boxHeight + 'px' }"
  8. ></canvas>
  9. <image
  10. v-if="imgSrc"
  11. :src="imgSrc"
  12. @load="myLucky.clearCanvas()"
  13. :style="{ width: boxWidth + 'px', height: boxHeight + 'px' }"
  14. ></image>
  15. <!-- #ifdef APP-PLUS -->
  16. <view v-if="btnShow">
  17. <view class="lucky-grid-btn" v-for="(btn, index) in btns" :key="index" @click="toPlay(btn, index)" :style="{
  18. top: btn.top + 'px',
  19. left: btn.left + 'px',
  20. width: btn.width + 'px',
  21. height: btn.height + 'px',
  22. }"></view>
  23. </view>
  24. <!-- #endif -->
  25. <!-- #ifndef APP-PLUS -->
  26. <view v-if="btnShow">
  27. <cover-view class="lucky-grid-btn" v-for="(btn, index) in btns" :key="index" @click="toPlay(btn, index)" :style="{
  28. top: btn.top + 'px',
  29. left: btn.left + 'px',
  30. width: btn.width + 'px',
  31. height: btn.height + 'px',
  32. }"></cover-view>
  33. </view>
  34. <!-- #endif -->
  35. <!-- #ifndef H5 -->
  36. <view v-if="myLucky">
  37. <div class="lucky-imgs">
  38. <div v-for="(block, index) in blocks" :key="index">
  39. <div v-if="block.imgs">
  40. <div v-for="(img, i) in block.imgs" :key="i">
  41. <image :src="img.src" :data-index="index" :data-i="i" @load="e => imgBindload(e, 'blocks')"></image>
  42. <image :src="img.activeSrc" :data-index="index" :data-i="i" @load="e => imgBindloadActive(e, 'blocks')"></image>
  43. </div>
  44. </div>
  45. </div>
  46. </div>
  47. <div class="lucky-imgs">
  48. <div v-for="(prize, index) in prizes" :key="index">
  49. <div v-if="prize.imgs">
  50. <div v-for="(img, i) in prize.imgs" :key="i">
  51. <image :src="img.src" :data-index="index" :data-i="i" @load="e => imgBindload(e, 'prizes')"></image>
  52. <image :src="img.activeSrc" :data-index="index" :data-i="i" @load="e => imgBindloadActive(e, 'prizes')"></image>
  53. </div>
  54. </div>
  55. </div>
  56. </div>
  57. <div class="lucky-imgs">
  58. <div v-for="(btn, index) in buttons" :key="index">
  59. <div v-if="btn.imgs">
  60. <image v-for="(img, i) in btn.imgs" :key="i" :src="img.src" :data-index="index" :data-i="i" @load="e => imgBindload(e, 'buttons')"></image>
  61. </div>
  62. </div>
  63. </div>
  64. <div class="lucky-imgs">
  65. <span v-if="button && button.imgs">
  66. <image v-for="(img, i) in button.imgs" :key="i" :src="img.src" :data-i="i" @load="e => imgBindloadBtn(e, 'button')"></image>
  67. </span>
  68. </div>
  69. </view>
  70. <!-- #endif -->
  71. </view>
  72. </template>
  73. <script>
  74. import { changeUnits, resolveImage, getImage } from './utils.js'
  75. import { LuckyGrid } from '../../lucky-canvas'
  76. export default {
  77. name: 'lucky-grid',
  78. data () {
  79. return {
  80. imgSrc: '',
  81. myLucky: null,
  82. canvas: null,
  83. isShow: false,
  84. boxWidth: 100,
  85. boxHeight: 100,
  86. dpr: 1,
  87. btns: [],
  88. btnShow: false,
  89. }
  90. },
  91. props: {
  92. width: {
  93. type: String,
  94. default: '600rpx'
  95. },
  96. height: {
  97. type: String,
  98. default: '600rpx'
  99. },
  100. cols: {
  101. type: [String, Number],
  102. default: 3,
  103. },
  104. rows: {
  105. type: [String, Number],
  106. default: 3,
  107. },
  108. blocks: {
  109. type: Array,
  110. default: () => []
  111. },
  112. prizes: {
  113. type: Array,
  114. default: () => []
  115. },
  116. buttons: {
  117. type: Array,
  118. default: () => []
  119. },
  120. button: {
  121. type: Object,
  122. default: undefined
  123. },
  124. defaultConfig: {
  125. type: Object,
  126. default: () => ({})
  127. },
  128. defaultStyle: {
  129. type: Object,
  130. default: () => ({})
  131. },
  132. activeStyle: {
  133. type: Object,
  134. default: () => ({})
  135. }
  136. },
  137. mounted () {
  138. // #ifdef APP-PLUS
  139. console.error('该抽奖插件的最新版暂不支持app端, 请通过npm安装旧版本【npm i uni-luck-draw@1.3.9】')
  140. // #endif
  141. // #ifndef APP-PLUS
  142. this.initLucky()
  143. // #endif
  144. },
  145. watch: {
  146. cols (newData) {
  147. this.myLucky && (this.myLucky.cols = newData)
  148. },
  149. rows (newData) {
  150. this.myLucky && (this.myLucky.rows = newData)
  151. },
  152. blocks (newData) {
  153. this.myLucky && (this.myLucky.blocks = newData)
  154. },
  155. prizes (newData) {
  156. this.myLucky && (this.myLucky.prizes = newData)
  157. },
  158. buttons (newData) {
  159. this.myLucky && (this.myLucky.buttons = newData)
  160. },
  161. button (newData) {
  162. this.myLucky && (this.myLucky.button = newData)
  163. },
  164. defaultStyle (newData) {
  165. this.myLucky && (this.myLucky.defaultStyle = newData)
  166. },
  167. defaultConfig (newData) {
  168. this.myLucky && (this.myLucky.defaultConfig = newData)
  169. },
  170. activeStyle (newData) {
  171. this.myLucky && (this.myLucky.activeStyle = newData)
  172. },
  173. },
  174. methods: {
  175. async imgBindload (res, name) {
  176. const { index, i } = res.currentTarget.dataset
  177. const img = this[name][index].imgs[i]
  178. resolveImage(img, this.canvas)
  179. },
  180. async imgBindloadActive (res, name) {
  181. const { index, i } = res.currentTarget.dataset
  182. const img = this[name][index].imgs[i]
  183. resolveImage(img, this.canvas, 'activeSrc', '$activeResolve')
  184. },
  185. async imgBindloadBtn (res, name) {
  186. const { i } = res.currentTarget.dataset
  187. const img = this[name].imgs[i]
  188. resolveImage(img, this.canvas)
  189. },
  190. getImage () {
  191. return getImage.call(this, 'lucky-grid', this.canvas)
  192. },
  193. hideCanvas () {
  194. // #ifdef MP
  195. this.getImage().then(res => {
  196. this.imgSrc = res.tempFilePath
  197. })
  198. // #endif
  199. },
  200. initLucky () {
  201. this.boxWidth = changeUnits(this.width)
  202. this.boxHeight = changeUnits(this.height)
  203. this.isShow = true
  204. // 某些情况下获取不到 canvas
  205. this.$nextTick(() => {
  206. setTimeout(() => {
  207. this.draw()
  208. })
  209. })
  210. },
  211. draw () {
  212. const _this = this
  213. uni.createSelectorQuery().in(this).select('#lucky-grid').fields({
  214. node: true, size: true
  215. }).exec((res) => {
  216. // #ifdef H5
  217. res[0].node = document.querySelector('#lucky-grid canvas')
  218. // #endif
  219. if (!res[0] || !res[0].node) return console.error('lucky-canvas 获取不到 canvas 标签')
  220. const { node, width, height } = res[0]
  221. const canvas = this.canvas = node
  222. const ctx = this.ctx = canvas.getContext('2d')
  223. const dpr = this.dpr = uni.getSystemInfoSync().pixelRatio
  224. // #ifndef H5
  225. canvas.width = width * dpr
  226. canvas.height = height * dpr
  227. ctx.scale(dpr, dpr)
  228. // #endif
  229. const myLucky = this.myLucky = new LuckyGrid({
  230. // #ifdef H5
  231. flag: 'WEB',
  232. // #endif
  233. // #ifdef MP
  234. flag: 'MP-WX',
  235. // #endif
  236. ctx,
  237. dpr,
  238. setTimeout,
  239. clearTimeout,
  240. setInterval,
  241. clearInterval,
  242. // #ifdef H5
  243. rAF: requestAnimationFrame,
  244. // #endif
  245. unitFunc: (num, unit) => changeUnits(num + unit),
  246. afterInit: function () {
  247. [..._this.$props.buttons, _this.$props.button].forEach((btn, index) => {
  248. if (!btn) return
  249. const [left, top, width, height] = this.getGeometricProperty([
  250. btn.x,
  251. btn.y,
  252. btn.col || 1,
  253. btn.row || 1
  254. ])
  255. _this.btns[index] = { top, left, width, height }
  256. })
  257. _this.$forceUpdate()
  258. },
  259. afterStart: () => {
  260. this.imgSrc = ''
  261. },
  262. }, {
  263. ...this.$props,
  264. width,
  265. height,
  266. start: (...rest) => {
  267. this.$emit('start', ...rest)
  268. },
  269. end: (...rest) => {
  270. this.$emit('end', ...rest)
  271. this.hideCanvas()
  272. },
  273. })
  274. this.btnShow = true
  275. })
  276. },
  277. toPlay (btn, index) {
  278. this.myLucky.startCallback(btn, this.$props.buttons[index])
  279. },
  280. init () {
  281. this.myLucky.init()
  282. },
  283. play (...rest) {
  284. this.myLucky.play(...rest)
  285. },
  286. stop (...rest) {
  287. this.myLucky.stop(...rest)
  288. },
  289. },
  290. }
  291. </script>
  292. <style scoped>
  293. .lucky-box {
  294. position: relative;
  295. overflow: hidden;
  296. margin: 0 auto;
  297. }
  298. .lucky-box canvas {
  299. position: absolute;
  300. pointer-events: none;
  301. left: 0;
  302. top: 0;
  303. }
  304. .lucky-grid-btn {
  305. position: absolute;
  306. background: rgba(0, 0, 0, 0);
  307. border-radius: 0;
  308. cursor: pointer;
  309. }
  310. .lucky-imgs {
  311. width: 0;
  312. height: 0;
  313. visibility: hidden;
  314. }
  315. </style>