vue 中实现刮刮卡
先看一下效果图:
ps: 图片有晃动的效果,是博主截图大小不一致造成的,以至于合成的gif图有晃动的效果,实际页面中是没有的,请勿担心哦
实现的原理:
1.通过canvas标签属性进行绘画实现
2.实际有3层,底层是存放奖品名称,中层是canvas画出的刮层,表层是那个红色的礼节 通过absolute 绝对定位实现重叠效果(单独切红色礼节,就是避免计算canvas特殊形状,只需要简单地举行即可)
3.首次看会以为我的canvas底部画的是不规则的形状,其实不然,canvas刮层画的是矩形,上面通过单独把 红色礼节 切图出来,定位上去的
上代码:
1.html :
<template> <div class="container" id="top"> <div class="award_box"> <div class="award" v-if="showPrize"> <div class="title"> 恭喜您获得 <span>198元享受580元(烫/染2选1)优惠券</span> </div> </div> <div class="surface"></div> <canvas id="c1" class="canvas" @touchmove="touchmove" @touchstart="touchstart" @touchend="touchend" v-show="!isScratch" ></canvas> </div> </div> </template>
2.js:
export default { name: "scratchCard", data() { return { c1: "", //画布 ctx: "", //画笔 ismousedown: false, //标志用户是否按下鼠标或开始触摸 fontem: "", // 获取html字体大小 isScratch: false, // 是否刮过卡 showPrize: false // 显示奖品 }; }, mounted() { this.fontem = parseInt( window.getComputedStyle(document.documentElement, null)["font-size"] ); //这是为了不同分辨率上配合@media自动调节刮的宽度 this.c1 = document.getElementById("c1"); //这里很关键,canvas自带两个属性width、height,我理解为画布的分辨率,跟style中的width、height意义不同。 //最好设置成跟画布在页面中的实际大小一样 //不然canvas中的坐标跟鼠标的坐标无法匹配 this.c1.width = this.c1.clientWidth + 15 this.c1.height = this.c1.clientHeight; this.ctx = this.c1.getContext("2d"); this.initCanvas(); }, methods: { // 画刮刮卡 initCanvas() { this.ctx.globalCompositeOperation = "source-over"; this.ctx.fillStyle = "#e5e5e5"; this.ctx.fillRect(0, 0, this.c1.clientWidth, this.c1.clientHeight); this.ctx.fill(); this.ctx.font = "Bold 24px Arial"; this.ctx.textAlign = "center"; this.ctx.fillStyle = "#a0a0a0"; this.ctx.fillText("刮开有奖", this.c1.width / 2, 55); //有些老的手机自带浏览器不支持destination-out,下面的代码中有修复的方法 this.ctx.globalCompositeOperation = "destination-out"; }, touchstart(e) { e.preventDefault(); this.ismousedown = true; }, // 操作刮卡 touchend(e) { sessionStorage.setItem('isScratch',true) e.preventDefault(); //得到canvas的全部数据 var a = this.ctx.getImageData(0, 0, this.c1.width, this.c1.height); var j = 0; for (var i = 3; i < a.data.length; i += 4) { if (a.data[i] == 0) j++; } //当被刮开的区域等于一半时,则可以开始处理结果 if (j >= a.data.length / 8) { this.isScratch = true; } this.ismousedown = false; }, touchmove(e) { this.showPrize = true e.preventDefault(); if (this.ismousedown) { if (e.changedTouches) { e = e.changedTouches[e.changedTouches.length - 1]; } var topY = document.getElementById("top").offsetTop; var oX = this.c1.offsetLeft, oY = this.c1.offsetTop + topY; var x = (e.clientX + document.body.scrollLeft || e.pageX) - oX || 0, y = (e.clientY + document.body.scrollTop || e.pageY) - oY || 0; //画360度的弧线,就是一个圆,因为设置了ctx.globalCompositeOperation = 'destination-out'; //画出来是透明的 this.ctx.beginPath(); this.ctx.arc(x, y, this.fontem * 0.5, 0, Math.PI * 2, true); // 调整画笔的大小 //下面3行代码是为了修复部分手机浏览器不支持destination-out //我也不是很清楚这样做的原理是什么 // this.c1.style.display = 'none'; // this.c1.offsetHeight; // this.c1.style.display = 'inherit'; this.ctx.fill(); } } } };
3.css:
.container { width: 100%; height: 100%; background-color: #985113; padding:100px 0; .award_box { width: 95%; height: 360px; margin: 0px auto; background: url("./img/2.img") no-repeat center/100%; padding-top: 70px; .award { width: 85%; height: 180px; position: absolute; left: 50%; transform: translateX(-50%); width: 68%; .title { color: rgba(3, 45, 97, 1); font-size: 32px; font-weight: 800; line-height: 50px; margin-top: 50px; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; span { color: #cc162d; } } } .surface { position: absolute; z-index: 98; width: 95%; height: 160px; margin: 150px auto; background: url("./img/1.png") no-repeat center/100%; padding-top: 70px; } .canvas { position: relative; } } .map_box{ margin-top: 200px; width: 100%; height: 500px; padding:10px; bottom:5px solid orange; box-sizing:border-box; } }