• uniapp小程序刮刮乐抽奖


     使用canvas画布画出刮刮乐要被刮的图片,使用移动清除画布。

    当前代码封装为刮刮乐的组件;

    vue代码: 

    1. <script>
    2. import { luckDrawInfo } from '@/api/luckDraw.js';
    3. import colors from '@/mixins/color';
    4. export default {
    5. mixins: [colors],
    6. data() {
    7. return {//https://cdn.dev.scrm.juplus.cn/InQLzDLoAl2S9LyNJUXQ45gpA.png
    8. mask: true,
    9. wtf:true,
    10. luckDrawInfo: {},
    11. rule_show:false,
    12. result_show:false,
    13. prize_show:false,
    14. total:0,
    15. currentPrize:{},
    16. list:[],
    17. id: "",
    18. prizeTitle: "",
    19. filePath: "",
    20. ctx: null,
    21. width: 0,
    22. height: 0,
    23. disabled: false, // 是否禁止刮卡
    24. readyState: false, // 是否开始绘制
    25. endState: false, // 结束刮卡状态
    26. watermark: '刮一刮', // 水印文字
    27. watermarkColor: '#c5c5c5', // 水印文字颜色
    28. watermarkSize: 14, // 水印文字大小
    29. title: '刮一刮开奖', // 提示文字
    30. titleColor: '#888', // 提示文字颜色
    31. titleSize: 24, // 提示文字大小
    32. startX: 0, // 触摸x轴位置
    33. startY: 0, // 触摸y轴位置
    34. touchSize: 30, // 触摸画笔大小
    35. percentage: 50, // 刮开百分之多少的时候开奖
    36. }
    37. },
    38. props: {
    39. userId: {
    40. type: [Number,String]
    41. },
    42. type:{
    43. type: [Number,String]
    44. }
    45. },
    46. //渲染完了
    47. mounted() {
    48. this.id = this.userId;
    49. this.init();
    50. },
    51. methods: {
    52. drawInit(imgUrl) {
    53. this.endState = false;
    54. this.readyState = false;
    55. this.ctx.clearRect(0, 0, this.width, this.height); // 清除画布上在该矩形区域内的内容(x,y,宽,高)。
    56. // this.ctx.setFillStyle('#ddd'); // 填充颜色
    57. // this.ctx.fillRect(0, 0, this.width, this.height); // 填充区域(x,y,宽,高)
    58. /**
    59. * 绘制文字水印
    60. */
    61. // var width = this.watermark.length * this.watermarkSize;
    62. // this.ctx.save(); // 保存当前的绘图上下文。
    63. // this.ctx.rotate(-10 * Math.PI / 180); // 以原点为中心,原点可以用 translate方法修改。顺时针旋转当前坐标轴。多次调用rotate,旋转的角度会叠加。
    64. // let x = 0;
    65. // let y = 0;
    66. // let i = 0;
    67. // while ((x <= this.width * 5 || y <= this.height * 5) && i < 300) {
    68. // this.ctx.setFillStyle(this.watermarkColor); // 填充颜色
    69. // this.ctx.setFontSize(this.watermarkSize); // 设置字体的字号
    70. // this.ctx.fillText(this.watermark, x, y); // 填充的文本(文字,x,y)
    71. // x += width + width * 1.6;
    72. // if (x > this.width && y <= this.height) {
    73. // x = -Math.random() * 100;
    74. // y += this.watermarkSize * 3;
    75. // }
    76. // i++;
    77. // }
    78. // this.ctx.restore(); // 恢复之前保存的绘图上下文。
    79. /**
    80. * 绘制标题
    81. */
    82. // this.ctx.setTextAlign("center"); // 用于设置文字的对齐
    83. // this.ctx.setTextBaseline("middle"); // 用于设置文字的水平对齐
    84. // this.ctx.setFillStyle(this.titleColor); // 填充颜色
    85. // this.ctx.setFontSize(this.titleSize); // 设置字体的字号
    86. // this.ctx.fillText(this.title, this.width / 2, this.height / 2); // 填充的文本(文字,x,y)
    87. /**
    88. * 绘制图片
    89. */
    90. this.ctx.drawImage(this.filePath, 0, 0, this.width, this.height);
    91. this.ctx.draw(); // 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中。
    92. this.readyState = true; // 完成绘制
    93. },
    94. // 手指触摸动作开始
    95. touchstart(e) {
    96. if (this.disabled || this.endState) {
    97. return;
    98. }
    99. this.startPlay();
    100. this.startX = e.touches[0].x;
    101. this.startY = e.touches[0].y;
    102. },
    103. // 手指触摸后移动
    104. touchmove(e) {
    105. if (this.disabled || this.endState) return;
    106. if (!this.prizeTitle) return;
    107. this.ctx.clearRect(this.startX, this.startY, this.touchSize, this.touchSize); // 清除画布上在该矩形区域内的内容(x,y,宽,高)。
    108. this.ctx.draw(true); // false:本次绘制是否接着上一次绘制,true:保留当前画布上的内容
    109. //记录移动点位
    110. this.startX = e.touches[0].x;
    111. this.startY = e.touches[0].y;
    112. },
    113. // 手指触摸动作结束
    114. touchend(e) {
    115. if (this.disabled || this.endState) {
    116. return;
    117. }
    118. // 返回一个数组,用来描述 canvas 区域隐含的像素数据,在自定义组件下,第二个参数传入自定义组件实例 this,以操作组件内 组件。
    119. uni.canvasGetImageData({
    120. canvasId: 'myCanvas',
    121. x: 0,
    122. y: 0,
    123. width: this.width,
    124. height: this.height,
    125. success: (res) => {
    126. console.log(res);
    127. let pixels = res.data;
    128. let transPixels = [];
    129. for (let i = 0; i < pixels.length; i += 4) {
    130. if (pixels[i + 3] < 128) {
    131. transPixels.push(pixels[i + 3]);
    132. }
    133. }
    134. var percent = (transPixels.length / (pixels.length / 4) * 100).toFixed(2);
    135. if (percent >= this.percentage) {
    136. this.scrapingSuccess();
    137. }
    138. },
    139. fail: (e) => {
    140. console.log(e);
    141. },
    142. }, this);
    143. },
    144. // 成功,清除所有图层
    145. scrapingSuccess(e) {
    146. if (this.endState) {
    147. return;
    148. }
    149. this.endState = true;
    150. this.ctx.moveTo(0, 0); // 把路径移动到画布中的指定点,不创建线条。用 stroke() 方法来画线条。
    151. this.ctx.clearRect(0, 0, this.width, this.height); // 清除画布上在该矩形区域内的内容(x,y,宽,高)。
    152. this.ctx.stroke(); // 画出当前路径的边框。默认颜色色为黑色。
    153. this.ctx.draw(true);
    154. // 弹出奖品
    155. setTimeout(()=>{
    156. this.result_show = true;
    157. this.drawInit();
    158. this.wtf = true;
    159. this.prizeTitle = "";
    160. },800)
    161. },
    162. init(){
    163. if(this.userInfo){
    164. this.getInfo()
    165. }else{
    166. setTimeout(()=>{
    167. this.init()
    168. },500)
    169. }
    170. },
    171. getInfo(){
    172. luckDrawInfo.getDetail({id:this.id}).then(res => {
    173. this.luckDrawInfo=res.data
    174. this.total=res.data.my_can_num
    175. this.action('lottery',this.id,0,2,this.luckDrawInfo.title,'','lottery')
    176. let content = uni.createSelectorQuery().in(this).select(".scrapingBoxContent");
    177. content.boundingClientRect((data) => {
    178. this.width = data.width;
    179. this.height = data.height;
    180. this.ctx = uni.createCanvasContext('myCanvas', this);
    181. uni.getImageInfo({
    182. src: this.merchantInfo.cdn_static + 'statistics/luckDrawImg/scratchcard/scratchingBefore.png',
    183. success: (res) => {
    184. this.filePath = res.path;
    185. this.drawInit();
    186. }
    187. })
    188. }).exec()
    189. })
    190. },
    191. choiseAddress(){
    192. this.currentPrize.is_address==1?uni.navigateTo({
    193. url:'/pages/address/address'
    194. }):''
    195. this.result_show=false
    196. },
    197. choiseAddress1(data){
    198. this.currentPrize=data
    199. uni.navigateTo({
    200. url:'/pages/address/address'
    201. })
    202. this.prize_show=false
    203. },
    204. setAddress(id){
    205. luckDrawInfo.setAddress({address_id:id,history_id:this.currentPrize.history_id||this.currentPrize.id}).then(res => {
    206. uni.showToast({
    207. title:"地址设置成功",
    208. icon:'none'
    209. })
    210. })
    211. },
    212. getRule(){
    213. if (this.prizeTitle) {
    214. this.scrapingSuccess();
    215. setTimeout(()=>{
    216. this.rule_show = true;
    217. },800)
    218. } else {
    219. this.rule_show = true;
    220. }
    221. },
    222. getResult(){
    223. // if(!this.wtf){
    224. // return false
    225. // }
    226. if (this.prizeTitle) {
    227. this.scrapingSuccess();
    228. } else {
    229. luckDrawInfo.getResult({lottery_id:this.id}).then(res => {
    230. this.list=res.data.data
    231. this.prize_show=true
    232. });
    233. }
    234. },
    235. // 点击开始,请求接口抽奖
    236. startPlay(index) {
    237. if(this.luckDrawInfo.is_register==1&&!this.userInfo.type){
    238. uni.navigateTo({
    239. url:'/pages/login/login'
    240. })
    241. return false
    242. }
    243. if(this.luckDrawInfo.is_form==1&&this.luckDrawInfo.user_form_count==0){
    244. uni.navigateTo({
    245. url:'/pages/form/form?id='+this.luckDrawInfo.form_id+'&type_id=' + this.id + '&type=lottery'
    246. })
    247. return false
    248. }
    249. if(!this.wtf){
    250. return false
    251. }
    252. // 活动未开始或活动已结束
    253. let startTimeMs = new Date(this.luckDrawInfo.start_time).getTime();
    254. let endTimeMs = new Date(this.luckDrawInfo.end_time).getTime();
    255. let nowTimeMs = new Date().getTime();
    256. if (nowTimeMs < startTimeMs) {
    257. uni.showToast({
    258. icon: "none",
    259. title: "活动未开始"
    260. })
    261. return false;
    262. }
    263. if (nowTimeMs > endTimeMs) {
    264. uni.showToast({
    265. icon: "none",
    266. title: "活动已结束"
    267. })
    268. return false;
    269. }
    270. this.mask = false;
    271. this.wtf = false;
    272. luckDrawInfo.run({id:this.id}).then(res => {
    273. this.currentPrize = res.data;
    274. this.total = res.data.row_lottery_new.my_can_num;
    275. this.prizeTitle = res.data.title;
    276. }).catch(err => {
    277. this.wtf = true;
    278. });
    279. }
    280. }
    281. }
    282. script>
    283. <style scoped lang="scss">
    284. @import 'index.scss';
    285. /**/
    286. style>

    scss代码:

    1. .page{
    2. width: 750rpx;
    3. min-height: 100vh;
    4. height: 1448rpx;
    5. position: relative;
    6. }
    7. .bg{
    8. width: 750rpx;
    9. min-height: 100vh;
    10. height: 1448rpx;
    11. }
    12. .content{
    13. width: 750rpx;
    14. min-height: 100vh;
    15. height: 1448rpx;
    16. position: absolute;
    17. top: 0;
    18. left: 0;
    19. }
    20. .logo{
    21. height: 60rpx;
    22. display: flex;
    23. justify-content: center;
    24. margin-top: 90rpx;
    25. image{
    26. height: 60rpx;
    27. }
    28. }
    29. .title{
    30. height: 254rpx;
    31. display: flex;
    32. justify-content: center;
    33. margin-top: 20rpx;
    34. image{
    35. width: 640rpx;
    36. height: 254rpx;
    37. }
    38. }
    39. .notification{
    40. width: 370rpx;
    41. height: 56rpx;
    42. display: flex;
    43. justify-content: space-between;
    44. align-items: center;
    45. margin: -60rpx auto 0 auto;
    46. view{
    47. width: 26rpx;
    48. height: 4rpx;
    49. background-color: #fff;
    50. }
    51. text{
    52. font-size: 38rpx;
    53. font-family: PingFangSC-Medium, PingFang SC;
    54. font-weight: 500;
    55. color: #FFFFFF;
    56. }
    57. }
    58. .box{
    59. width: 658rpx;
    60. height: 422rpx;
    61. margin: 110rpx auto 0 auto;
    62. position: relative;
    63. .scrapingBg{
    64. width: 100%;
    65. height: 100%;
    66. }
    67. .scrapingBox{
    68. width: 100%;
    69. height: 100%;
    70. position: absolute;
    71. left: 0;
    72. top: 0;
    73. box-sizing: border-box;
    74. padding: 52rpx 62rpx;
    75. .scrapingBoxContent{
    76. width: 536rpx;
    77. height: 318rpx;
    78. z-index: 2;
    79. position: relative;
    80. view {
    81. width: 100%;
    82. height: 100%;
    83. box-sizing: border-box;
    84. padding: 52rpx 116rpx;
    85. display: flex;
    86. justify-content: center;
    87. align-items: center;
    88. font-size: 72rpx;
    89. font-family: PingFangSC-Semibold, PingFang SC;
    90. font-weight: 600;
    91. color: #1B9AF9;
    92. text-shadow: 0px 4px 8px rgba(149,216,255,0.5);
    93. }
    94. }
    95. }
    96. }
    97. .count{
    98. display: flex;
    99. justify-content: center;
    100. margin-top: 44rpx;
    101. .tip{
    102. font-size: 28rpx;
    103. font-family: PingFangSC-Medium, PingFang SC;
    104. font-weight: 500;
    105. color: #777777;
    106. text{
    107. color: #0039AF;
    108. }
    109. }
    110. }
    111. .btns{
    112. display: flex;
    113. align-items: center;
    114. justify-content: space-between;
    115. margin-top: 26rpx;
    116. }
    117. .btn{
    118. width: 376rpx;
    119. height: 166rpx;
    120. position: relative;
    121. .btnImg{
    122. width: 100%;
    123. height: 100%;
    124. }
    125. .btnConent{
    126. width: 100%;
    127. position: absolute;
    128. top: 50%;
    129. transform: translateY(-100%);
    130. display: flex;
    131. justify-content: center;
    132. align-items: center;
    133. image{
    134. width: 42rpx;
    135. height: 42rpx;
    136. margin-right: 12rpx;
    137. }
    138. text{
    139. font-size: 42rpx;
    140. font-family: PingFangSC-Medium, PingFang SC;
    141. font-weight: 500;
    142. color: #FFFFFF;
    143. }
    144. }
    145. }
    146. .win{
    147. width: 750rpx;
    148. height: 100vh;
    149. background: rgba(0, 0, 0, 0.8);
    150. position: fixed;
    151. top: 0;
    152. left: 0;
    153. z-index: 2;
    154. display: flex;
    155. flex-direction: column;
    156. align-items: center;
    157. justify-content: center;
    158. }
    159. .win_box{
    160. width: 662rpx;
    161. height: 60%;
    162. padding: 40rpx;
    163. box-sizing: border-box;
    164. border-radius: 24rpx;
    165. }
    166. .win_box_bg{
    167. background: #C3E5FE;
    168. }
    169. .bg3{
    170. background: #C3E5FE;
    171. }
    172. .iconcolseIcon{
    173. font-size: 58rpx;
    174. margin-top: 98rpx;
    175. }
    176. .win_box1{
    177. width: 630rpx;
    178. height: 922rpx;
    179. position: relative;
    180. }
    181. .win_bg{
    182. width: 630rpx;
    183. height: 922rpx;
    184. }
    185. .win_content{
    186. width: 630rpx;
    187. height: 922rpx;
    188. position: absolute;
    189. left: 0;
    190. top: 0;
    191. display: flex;
    192. flex-direction: column;
    193. align-items: center;
    194. }
    195. .win_tips{
    196. font-size: 48rpx;
    197. font-family: SourceHanSansSC-Medium, SourceHanSansSC;
    198. font-weight: bold;
    199. margin-top: 290rpx;
    200. }
    201. .win_title{
    202. font-size: 48rpx;
    203. font-family: SourceHanSansSC-Medium, SourceHanSansSC;
    204. font-weight: bold;
    205. color: #FE6631;
    206. margin: 170rpx 0;
    207. }
    208. .win_btn{
    209. width: 280rpx;
    210. height: 80rpx;
    211. line-height: 80rpx;
    212. text-align: center;
    213. background: #FFE047;
    214. border-radius: 46rpx;
    215. font-size: 32rpx;
    216. font-family: SourceHanSansSC-Medium, SourceHanSansSC;
    217. font-weight: bold;
    218. color: #13112C;
    219. }
    220. .win_tit{
    221. font-size: 48rpx;
    222. font-family: SourceHanSansSC-Medium, SourceHanSansSC;
    223. font-weight: bold;
    224. margin-bottom: 32rpx;
    225. }
    226. .win_box2{
    227. width: 662rpx;
    228. height: 900rpx;
    229. background: #FFFFFF;
    230. border-radius: 24rpx;
    231. display: flex;
    232. flex-direction: column;
    233. }
    234. .items{
    235. width: 662rpx;
    236. height: 108rpx;
    237. background: #C3E5FE;
    238. border-radius: 24rpx 24rpx 0rpx 0rpx;
    239. display: flex;
    240. align-items: center;
    241. flex-shrink:0
    242. }
    243. .left,.right{
    244. width: 50%;
    245. text-align: center;
    246. font-family: SourceHanSansSC-Medium, SourceHanSansSC;
    247. font-weight: bold;
    248. }
    249. .i_title{
    250. font-size: 36rpx;
    251. }
    252. .list{
    253. height: 792rpx;
    254. padding-bottom: 20rpx;
    255. overflow: hidden;
    256. }
    257. .item{
    258. width: 662rpx;
    259. height: 88rpx;
    260. display: flex;
    261. align-items: center;
    262. justify-content: space-around;
    263. }
    264. .item:nth-child(2n){
    265. background-color: #F4F4F4;
    266. }
    267. .r_btn{
    268. width: 160rpx;
    269. height: 60rpx;
    270. line-height: 60rpx;
    271. text-align: center;
    272. background: #FFC659;
    273. border-radius: 46rpx;
    274. font-family: SourceHanSansSC-Medium, SourceHanSansSC;
    275. font-weight: bold;
    276. font-size: 32rpx;
    277. margin:0 auto;
    278. }

    效果:

  • 相关阅读:
    nginx转发https:SSL_do_handshake() failed
    疆御3行业版无人机基础飞行操作教程,教程适用于DJI Mavic 3E、DJI Mavic 3T
    LuatOS-SOC接口文档(air780E)-- fonts - 字体库
    【python】list 删除列表中某个元素的3种方法;附加删除numpy数组中的指定索引元素的方法
    每天一道大厂SQL题【Day26】脉脉真题实战(二)活跃时长的均值
    【面试题】如何去掉vue的url地址中的#号?及其原理?
    Docker学习笔记
    Java里使用AspectJ实现AOP
    kotlin基础教程:<4>内置函数的使用
    深入体验Java Web开发内目-核心基础 PDF篇
  • 原文地址:https://blog.csdn.net/qq_43235503/article/details/134211690