使用canvas画布画出刮刮乐要被刮的图片,使用移动清除画布。
当前代码封装为刮刮乐的组件;
vue代码:
- <view class="page" v-if="merchantInfo.cdn_static">
- <image class="bg" :src="merchantInfo.cdn_static +'statistics/luckDrawImg/scratchcard/page_bg.png'" mode="aspectFill">image>
- <view class="content">
- <view class="logo">
- <image :src="merchantInfo.logo" mode="heightFix">image>
- view>
- <view class="title">
- <image :src="merchantInfo.cdn_static +'statistics/luckDrawImg/scratchcard/title.png'" mode="heightFix">image>
- view>
- <view class="notification">
- <view>view>
- <text>每日刮卡抽好礼text>
- <view>view>
- view>
- <view class="box">
- <image class="scrapingBg" :src="merchantInfo.cdn_static +'statistics/luckDrawImg/scratchcard/scrapingBg.png'">image>
- <view class="scrapingBox">
- <view class="scrapingBoxContent">
-
- <view>{{ prizeTitle || "" }}view>
- <canvas :style="{'width':width+'px','height':height+'px'}" style="position: absolute; top: 0;" canvas-id="myCanvas" id="myCanvas" @touchstart="touchstart" @touchend="touchend" @touchmove="touchmove">canvas>
- view>
- view>
- view>
- <view class="count">
- <view class="tip">您今天还有<text>{{total}}text>次抽奖机会view>
- view>
- <view class="btns">
- <view class="btn" @click="getRule">
- <image class="btnImg" :src="merchantInfo.cdn_static +'statistics/luckDrawImg/scratchcard/rule.png'">image>
- <view class="btnConent">
- <image :src="merchantInfo.cdn_static +'statistics/luckDrawImg/scratchcard/ruleIcon.png'">image>
- <text>查看规则text>
- view>
- view>
- <view class="btn" @click="getResult()">
- <image class="btnImg" :src="merchantInfo.cdn_static +'statistics/luckDrawImg/scratchcard/prize.png'">image>
- <view class="btnConent">
- <image :src="merchantInfo.cdn_static +'statistics/luckDrawImg/scratchcard/prizeIcon.png'">image>
- <text>兑换福利text>
- view>
- view>
- view>
- view>
- <view class="win" v-if="rule_show">
- <scroll-view scroll-y class="win_box win_box_bg">
- <mp-html :content="luckDrawInfo.rule" />
- scroll-view>
- <text class="iconfont iconcolseIcon theme-font-white" @click="rule_show=false">text>
- view>
- <view class="win" v-if="result_show">
- <view class="win_box1">
- <image class="win_bg" :src="merchantInfo.cdn_static +'statistics/luckDrawImg/result_bg.png'" mode="">image>
- <view class="win_content">
- <view class="win_tips theme-font-white">{{currentPrize.desc}}view>
- <view class="win_title">{{currentPrize.title}}view>
- <view class="win_btn" @click="choiseAddress()">{{currentPrize.is_address==1?'选择地址':'确定'}}view>
- view>
- view>
- view>
- <view class="win" v-if="prize_show">
- <view class="win_tit theme-font-white">我的奖品view>
- <view class="win_box2">
- <view class="items">
- <view class="left i_title">奖品view>
- <view class="right i_title">中奖时间view>
- view>
- <scroll-view scroll-y class="list">
- <view class="item" v-for="(item,index) in list" :key="index">
- <view class="left">{{item.lottery_prize_title}}view>
- <view class="right" v-if="item.is_address==1&&!item.address_id">
- <view class="r_btn" @click="choiseAddress1(item)">去领奖view>
- view>
- <view class="right" v-else>{{item.created_time}}view>
- view>
- scroll-view>
- view>
- <text class="iconfont iconcolseIcon theme-font-white" @click="prize_show=false">text>
- view>
- view>
-
-
- <script>
- import { luckDrawInfo } from '@/api/luckDraw.js';
- import colors from '@/mixins/color';
- export default {
- mixins: [colors],
- data() {
- return {//https://cdn.dev.scrm.juplus.cn/InQLzDLoAl2S9LyNJUXQ45gpA.png
- mask: true,
- wtf:true,
- luckDrawInfo: {},
- rule_show:false,
- result_show:false,
- prize_show:false,
- total:0,
- currentPrize:{},
- list:[],
- id: "",
- prizeTitle: "",
- filePath: "",
- ctx: null,
- width: 0,
- height: 0,
- disabled: false, // 是否禁止刮卡
- readyState: false, // 是否开始绘制
- endState: false, // 结束刮卡状态
- watermark: '刮一刮', // 水印文字
- watermarkColor: '#c5c5c5', // 水印文字颜色
- watermarkSize: 14, // 水印文字大小
- title: '刮一刮开奖', // 提示文字
- titleColor: '#888', // 提示文字颜色
- titleSize: 24, // 提示文字大小
- startX: 0, // 触摸x轴位置
- startY: 0, // 触摸y轴位置
- touchSize: 30, // 触摸画笔大小
- percentage: 50, // 刮开百分之多少的时候开奖
- }
- },
- props: {
- userId: {
- type: [Number,String]
- },
- type:{
- type: [Number,String]
- }
- },
- //渲染完了
- mounted() {
- this.id = this.userId;
- this.init();
- },
- methods: {
- drawInit(imgUrl) {
- this.endState = false;
- this.readyState = false;
- this.ctx.clearRect(0, 0, this.width, this.height); // 清除画布上在该矩形区域内的内容(x,y,宽,高)。
- // this.ctx.setFillStyle('#ddd'); // 填充颜色
- // this.ctx.fillRect(0, 0, this.width, this.height); // 填充区域(x,y,宽,高)
- /**
- * 绘制文字水印
- */
- // var width = this.watermark.length * this.watermarkSize;
- // this.ctx.save(); // 保存当前的绘图上下文。
- // this.ctx.rotate(-10 * Math.PI / 180); // 以原点为中心,原点可以用 translate方法修改。顺时针旋转当前坐标轴。多次调用rotate,旋转的角度会叠加。
- // let x = 0;
- // let y = 0;
- // let i = 0;
- // while ((x <= this.width * 5 || y <= this.height * 5) && i < 300) {
- // this.ctx.setFillStyle(this.watermarkColor); // 填充颜色
- // this.ctx.setFontSize(this.watermarkSize); // 设置字体的字号
- // this.ctx.fillText(this.watermark, x, y); // 填充的文本(文字,x,y)
- // x += width + width * 1.6;
- // if (x > this.width && y <= this.height) {
- // x = -Math.random() * 100;
- // y += this.watermarkSize * 3;
- // }
- // i++;
- // }
- // this.ctx.restore(); // 恢复之前保存的绘图上下文。
- /**
- * 绘制标题
- */
- // this.ctx.setTextAlign("center"); // 用于设置文字的对齐
- // this.ctx.setTextBaseline("middle"); // 用于设置文字的水平对齐
- // this.ctx.setFillStyle(this.titleColor); // 填充颜色
- // this.ctx.setFontSize(this.titleSize); // 设置字体的字号
- // this.ctx.fillText(this.title, this.width / 2, this.height / 2); // 填充的文本(文字,x,y)
-
- /**
- * 绘制图片
- */
- this.ctx.drawImage(this.filePath, 0, 0, this.width, this.height);
-
- this.ctx.draw(); // 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中。
- this.readyState = true; // 完成绘制
- },
- // 手指触摸动作开始
- touchstart(e) {
- if (this.disabled || this.endState) {
- return;
- }
- this.startPlay();
- this.startX = e.touches[0].x;
- this.startY = e.touches[0].y;
- },
- // 手指触摸后移动
- touchmove(e) {
- if (this.disabled || this.endState) return;
- if (!this.prizeTitle) return;
- this.ctx.clearRect(this.startX, this.startY, this.touchSize, this.touchSize); // 清除画布上在该矩形区域内的内容(x,y,宽,高)。
- this.ctx.draw(true); // false:本次绘制是否接着上一次绘制,true:保留当前画布上的内容
- //记录移动点位
- this.startX = e.touches[0].x;
- this.startY = e.touches[0].y;
- },
- // 手指触摸动作结束
- touchend(e) {
- if (this.disabled || this.endState) {
- return;
- }
- // 返回一个数组,用来描述 canvas 区域隐含的像素数据,在自定义组件下,第二个参数传入自定义组件实例 this,以操作组件内
- uni.canvasGetImageData({
- canvasId: 'myCanvas',
- x: 0,
- y: 0,
- width: this.width,
- height: this.height,
- success: (res) => {
- console.log(res);
- let pixels = res.data;
- let transPixels = [];
- for (let i = 0; i < pixels.length; i += 4) {
- if (pixels[i + 3] < 128) {
- transPixels.push(pixels[i + 3]);
- }
- }
- var percent = (transPixels.length / (pixels.length / 4) * 100).toFixed(2);
- if (percent >= this.percentage) {
- this.scrapingSuccess();
- }
- },
- fail: (e) => {
- console.log(e);
- },
- }, this);
- },
- // 成功,清除所有图层
- scrapingSuccess(e) {
- if (this.endState) {
- return;
- }
- this.endState = true;
- this.ctx.moveTo(0, 0); // 把路径移动到画布中的指定点,不创建线条。用 stroke() 方法来画线条。
- this.ctx.clearRect(0, 0, this.width, this.height); // 清除画布上在该矩形区域内的内容(x,y,宽,高)。
- this.ctx.stroke(); // 画出当前路径的边框。默认颜色色为黑色。
- this.ctx.draw(true);
- // 弹出奖品
- setTimeout(()=>{
- this.result_show = true;
- this.drawInit();
- this.wtf = true;
- this.prizeTitle = "";
- },800)
- },
- init(){
- if(this.userInfo){
- this.getInfo()
- }else{
- setTimeout(()=>{
- this.init()
- },500)
- }
- },
- getInfo(){
- luckDrawInfo.getDetail({id:this.id}).then(res => {
- this.luckDrawInfo=res.data
- this.total=res.data.my_can_num
- this.action('lottery',this.id,0,2,this.luckDrawInfo.title,'','lottery')
- let content = uni.createSelectorQuery().in(this).select(".scrapingBoxContent");
- content.boundingClientRect((data) => {
- this.width = data.width;
- this.height = data.height;
- this.ctx = uni.createCanvasContext('myCanvas', this);
- uni.getImageInfo({
- src: this.merchantInfo.cdn_static + 'statistics/luckDrawImg/scratchcard/scratchingBefore.png',
- success: (res) => {
- this.filePath = res.path;
- this.drawInit();
- }
- })
- }).exec()
- })
- },
- choiseAddress(){
- this.currentPrize.is_address==1?uni.navigateTo({
- url:'/pages/address/address'
- }):''
- this.result_show=false
- },
- choiseAddress1(data){
- this.currentPrize=data
- uni.navigateTo({
- url:'/pages/address/address'
- })
- this.prize_show=false
- },
- setAddress(id){
- luckDrawInfo.setAddress({address_id:id,history_id:this.currentPrize.history_id||this.currentPrize.id}).then(res => {
- uni.showToast({
- title:"地址设置成功",
- icon:'none'
- })
- })
- },
- getRule(){
- if (this.prizeTitle) {
- this.scrapingSuccess();
- setTimeout(()=>{
- this.rule_show = true;
- },800)
- } else {
- this.rule_show = true;
- }
- },
- getResult(){
- // if(!this.wtf){
- // return false
- // }
- if (this.prizeTitle) {
- this.scrapingSuccess();
- } else {
- luckDrawInfo.getResult({lottery_id:this.id}).then(res => {
- this.list=res.data.data
- this.prize_show=true
- });
- }
- },
- // 点击开始,请求接口抽奖
- startPlay(index) {
- if(this.luckDrawInfo.is_register==1&&!this.userInfo.type){
- uni.navigateTo({
- url:'/pages/login/login'
- })
- return false
- }
- if(this.luckDrawInfo.is_form==1&&this.luckDrawInfo.user_form_count==0){
- uni.navigateTo({
- url:'/pages/form/form?id='+this.luckDrawInfo.form_id+'&type_id=' + this.id + '&type=lottery'
- })
- return false
- }
- if(!this.wtf){
- return false
- }
-
- // 活动未开始或活动已结束
- let startTimeMs = new Date(this.luckDrawInfo.start_time).getTime();
- let endTimeMs = new Date(this.luckDrawInfo.end_time).getTime();
- let nowTimeMs = new Date().getTime();
- if (nowTimeMs < startTimeMs) {
- uni.showToast({
- icon: "none",
- title: "活动未开始"
- })
- return false;
- }
- if (nowTimeMs > endTimeMs) {
- uni.showToast({
- icon: "none",
- title: "活动已结束"
- })
- return false;
- }
-
- this.mask = false;
- this.wtf = false;
- luckDrawInfo.run({id:this.id}).then(res => {
- this.currentPrize = res.data;
- this.total = res.data.row_lottery_new.my_can_num;
- this.prizeTitle = res.data.title;
- }).catch(err => {
- this.wtf = true;
- });
- }
- }
- }
- script>
-
- <style scoped lang="scss">
- @import 'index.scss';
- /**/
- style>
scss代码:
-
- .page{
- width: 750rpx;
- min-height: 100vh;
- height: 1448rpx;
- position: relative;
- }
-
- .bg{
- width: 750rpx;
- min-height: 100vh;
- height: 1448rpx;
- }
-
- .content{
- width: 750rpx;
- min-height: 100vh;
- height: 1448rpx;
- position: absolute;
- top: 0;
- left: 0;
- }
-
-
- .logo{
- height: 60rpx;
- display: flex;
- justify-content: center;
- margin-top: 90rpx;
-
- image{
- height: 60rpx;
- }
- }
-
- .title{
- height: 254rpx;
- display: flex;
- justify-content: center;
- margin-top: 20rpx;
-
- image{
- width: 640rpx;
- height: 254rpx;
- }
- }
-
- .notification{
- width: 370rpx;
- height: 56rpx;
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin: -60rpx auto 0 auto;
-
- view{
- width: 26rpx;
- height: 4rpx;
- background-color: #fff;
- }
- text{
- font-size: 38rpx;
- font-family: PingFangSC-Medium, PingFang SC;
- font-weight: 500;
- color: #FFFFFF;
- }
- }
-
-
- .box{
- width: 658rpx;
- height: 422rpx;
- margin: 110rpx auto 0 auto;
- position: relative;
-
- .scrapingBg{
- width: 100%;
- height: 100%;
- }
-
- .scrapingBox{
- width: 100%;
- height: 100%;
- position: absolute;
- left: 0;
- top: 0;
- box-sizing: border-box;
- padding: 52rpx 62rpx;
-
- .scrapingBoxContent{
- width: 536rpx;
- height: 318rpx;
- z-index: 2;
- position: relative;
- view {
- width: 100%;
- height: 100%;
- box-sizing: border-box;
- padding: 52rpx 116rpx;
- display: flex;
- justify-content: center;
- align-items: center;
- font-size: 72rpx;
- font-family: PingFangSC-Semibold, PingFang SC;
- font-weight: 600;
- color: #1B9AF9;
- text-shadow: 0px 4px 8px rgba(149,216,255,0.5);
- }
- }
- }
- }
-
- .count{
- display: flex;
- justify-content: center;
- margin-top: 44rpx;
-
- .tip{
- font-size: 28rpx;
- font-family: PingFangSC-Medium, PingFang SC;
- font-weight: 500;
- color: #777777;
- text{
- color: #0039AF;
- }
- }
- }
-
- .btns{
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-top: 26rpx;
- }
-
- .btn{
- width: 376rpx;
- height: 166rpx;
- position: relative;
- .btnImg{
- width: 100%;
- height: 100%;
- }
-
- .btnConent{
- width: 100%;
- position: absolute;
- top: 50%;
- transform: translateY(-100%);
- display: flex;
- justify-content: center;
- align-items: center;
- image{
- width: 42rpx;
- height: 42rpx;
- margin-right: 12rpx;
- }
- text{
- font-size: 42rpx;
- font-family: PingFangSC-Medium, PingFang SC;
- font-weight: 500;
- color: #FFFFFF;
- }
- }
-
- }
-
-
- .win{
- width: 750rpx;
- height: 100vh;
- background: rgba(0, 0, 0, 0.8);
- position: fixed;
- top: 0;
- left: 0;
- z-index: 2;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- }
-
- .win_box{
- width: 662rpx;
- height: 60%;
- padding: 40rpx;
- box-sizing: border-box;
- border-radius: 24rpx;
- }
-
- .win_box_bg{
- background: #C3E5FE;
- }
-
- .bg3{
- background: #C3E5FE;
- }
-
- .iconcolseIcon{
- font-size: 58rpx;
- margin-top: 98rpx;
- }
-
- .win_box1{
- width: 630rpx;
- height: 922rpx;
- position: relative;
- }
-
- .win_bg{
- width: 630rpx;
- height: 922rpx;
- }
-
- .win_content{
- width: 630rpx;
- height: 922rpx;
- position: absolute;
- left: 0;
- top: 0;
- display: flex;
- flex-direction: column;
- align-items: center;
- }
-
- .win_tips{
- font-size: 48rpx;
- font-family: SourceHanSansSC-Medium, SourceHanSansSC;
- font-weight: bold;
- margin-top: 290rpx;
- }
-
- .win_title{
- font-size: 48rpx;
- font-family: SourceHanSansSC-Medium, SourceHanSansSC;
- font-weight: bold;
- color: #FE6631;
- margin: 170rpx 0;
- }
-
- .win_btn{
- width: 280rpx;
- height: 80rpx;
- line-height: 80rpx;
- text-align: center;
- background: #FFE047;
- border-radius: 46rpx;
- font-size: 32rpx;
- font-family: SourceHanSansSC-Medium, SourceHanSansSC;
- font-weight: bold;
- color: #13112C;
- }
-
- .win_tit{
- font-size: 48rpx;
- font-family: SourceHanSansSC-Medium, SourceHanSansSC;
- font-weight: bold;
- margin-bottom: 32rpx;
- }
-
- .win_box2{
- width: 662rpx;
- height: 900rpx;
- background: #FFFFFF;
- border-radius: 24rpx;
- display: flex;
- flex-direction: column;
- }
-
- .items{
- width: 662rpx;
- height: 108rpx;
- background: #C3E5FE;
- border-radius: 24rpx 24rpx 0rpx 0rpx;
- display: flex;
- align-items: center;
- flex-shrink:0
- }
-
- .left,.right{
- width: 50%;
- text-align: center;
- font-family: SourceHanSansSC-Medium, SourceHanSansSC;
- font-weight: bold;
- }
-
- .i_title{
- font-size: 36rpx;
- }
-
- .list{
- height: 792rpx;
- padding-bottom: 20rpx;
- overflow: hidden;
- }
-
- .item{
- width: 662rpx;
- height: 88rpx;
- display: flex;
- align-items: center;
- justify-content: space-around;
- }
-
- .item:nth-child(2n){
- background-color: #F4F4F4;
- }
-
- .r_btn{
- width: 160rpx;
- height: 60rpx;
- line-height: 60rpx;
- text-align: center;
- background: #FFC659;
- border-radius: 46rpx;
- font-family: SourceHanSansSC-Medium, SourceHanSansSC;
- font-weight: bold;
- font-size: 32rpx;
- margin:0 auto;
- }
效果: