如何实现刮刮卡效果

835 阅读4分钟

刮刮卡是活动的常用形式,用户通过类似刮奖的方式进行抽奖。

首先需要明白实现这样一个模块需要哪些步骤

1. 重置样式

2. 定义刮刮卡父级div

3. 定义刮刮卡默认设置 宽高 背景

4. 创建canvas 设置宽高 背景 id

5. 获取画布

6. 设置画笔颜色

7. 画笔透明

8. 监听touch

9. 滑动的时候用画笔画

需要初始化全局的一些数据 方便维护

    // 初始化选项
    let option = {      
        width: window.innerWidth,        // 刮刮卡的宽度      
        height: window.innerHeight / 2,  // 刮刮卡的高度      
        canvas: null,                    // 刮刮卡canvas元素挂载点      
        fatherNode: null,                // 刮刮卡父元素挂载点      
        count: 0,                        // 刮刮卡刮开的像素点      
        // 预加载的图      
        imgList: [        
            {          
                name: "bg",          // 一般是背景          
                url: ''        
            },        
            {          
                name: "cover",          
                // 一般是本地的同域名下的图          
                url: ""        
            }      
        ]    
    }

 在你处理图片的时候 需要处理这个图是否加载完了  这一步非必要 反正都是放在canvas底部的

// 预加载图
function preloadImg() {   
    return new Promise((resolve) => {     
        let list = option.imgList;     
        let count = 0;     
        list.forEach(e => {       
        let img = new Image();       
        img.src = e.url;       
        img.style.width = 0       
        img.style.height = 0       
        // 预加载图片要设block 否则会有默认占位       
        img.style.display = 'block';       
        img.onload = function () {         
            count++         
            if (count == option.imgList.length) {          
                resolve()        
            }       
        }      
        img.onerror = function () {        
        count++        
        if (count == option.imgList.length) {       
           resolve()       
        }      
    }       
    document.body.appendChild(img)     })
  }) 
}

接下来我们需要创建画布,做一些基础的设置画布的一些操作,并且正式把整个画布添加到页面中去

// 创建画布    
function createCanvas() {      
    // 创建父元素      
    option.fatherNode = document.createElement("div")      
    option.fatherNode.style.width = option.width || 300      
    option.fatherNode.style.height = option.height || 300      
    // 创建canvas      
    option.canvas = document.createElement("canvas")      
    option.canvas.setAttribute("id", "catCanvas")      
    option.canvas.width = option.width || 300      
    option.canvas.height = option.height || 300      
    // canvas底图      
    option.canvas.style.backgroundImage = 'url(' + option.imgList[0].url + ')';      
    option.canvas.style.backgroundSize = "100% 100%"      
    // option.canvas.style.backgroundColor = '#e9e9e9';      
    option.fatherNode.appendChild(option.canvas)      
    document.body.appendChild(option.fatherNode)    
}

处理一下刮刮卡蒙层的部分 要银灰色的那种

 // 设置canvas画笔    
function setCanvas() {      
    if (option.canvas.getContext('2d')) {        
        let ctx = option.canvas.getContext('2d');        
        // canvas的drawImage会跨域 要么就base64        
        // 绘制蒙层 + 文案        
        ctx.fillStyle = '#bbb';        
        ctx.fillRect(0, 0, option.width, option.height);        
        ctx.font = "Bold 20px Arial";        
        ctx.textAlign = "center";        
        ctx.fillStyle = "#e9e9e9";        
        ctx.fillText("刮卡赢大奖 回家吃鸡", 
        option.width / 2, 
        option.height / 2);      
    }    
}

关键一步来了 用户的画笔其实就是绘制的操作 只不过给绘制成了透明色

// 画笔    
function setCanvasTranparent() {     
    if (option.canvas.getContext('2d')) {        
        let ctx = option.canvas.getContext('2d');        
        // 画笔透明        
        ctx.globalCompositeOperation = "destination-out";      
    }    
}

接下来就是监听用户手势操作了

// 监听用户操作    
function listenUserTouch() {      
    // 开始的标识 你可以处理登录 可以利用tag值处理touchmove      
    option.canvas.addEventListener("touchstart", () => {        
        console.log("touchstart")      
    })

    option.canvas.addEventListener("touchmove", (e) => {        
        var touch = e.touches[0];        
        let x = touch.pageX;        
        let y = touch.pageY;        
        var p = option.fatherNode.getBoundingClientRect();        
        var fatherLeft = p.left;        
        var fatherTop = p.top;        
        let ctx = option.canvas.getContext('2d')        
        ctx.beginPath();        
        ctx.arc(x - fatherLeft, y - fatherTop, 20, 0, Math.PI * 2, true);        
        ctx.fillStyle = "#000000";        
        ctx.fill();      
    })      
    option.canvas.addEventListener("touchend", (e) => {        
        let ctx = option.canvas.getContext('2d')        
        var imageDate = ctx.getImageData(0, 0, option.width, option.height);        
        var data = imageDate.data;        
        for (let i = 0; i < data.length; i += 4) {          
            if (data[i + 3] < 128) {            
                option.count = option.count + 3;         
            }        
        }        
        var percent = (option.count / data.length).toFixed(2) * 100;        
        if (percent > 30) {          
            console.log('刮开了');          
            ctx.fillStyle = "rgba(255, 255, 255)";          
            ctx.fillRect(0, 0, option.width, option.height);        
        }      
    })    
}

最后就是组装起来 因为这些方法都是单独写的 

function loadCanvas() {  
    createCanvas()  
    setCanvas()  
    setCanvasTranparent()
    listenUserTouch()
}
window.onload = function () {  
    preloadImg().then(() => {
       loadCanvas()  
    })
}

tips: 目标常用api

矩形绘制

fillRect(x, y, width, height)

绘制一个填充的矩形

strokeRect(x, y, width, height)

绘制一个矩形的边框

clearRect(x, y, width, height)

清除指定矩形区域,让清除部分完全透明。

路径绘制

beginPath()

新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。

closePath()

闭合路径之后图形绘制命令又重新指向到上下文中。 不是必须要调用的

stroke()

通过线条来绘制图形轮廓。

fill()

通过填充路径的内容区域生成实心的图形。

移动画笔

moveTo(x,y)

将画笔移动到指定的坐标x以及y上 相当于把笔尖移动到那个位置 但是没有执行画的操作

绘制直线

lineTo(x, y)

绘制一条从当前位置到指定x以及y位置的直线。

绘制一个圆

arc(x, y, radius, startAngle, endAngle, anticlockwise)

画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。

arcTo(x1, y1, x2, y2, radius)

根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。

色彩的设置

fillStyle = color

设置图形的填充颜色。

strokeStyle = color

设置图形轮廓的颜色。

文字的绘制

fillText(text, x, y [, maxWidth])

在指定的(x,y)位置填充指定的文本,绘制的最大宽度是可选的.

strokeText(text, x, y [, maxWidth])

在指定的(x,y)位置绘制文本边框,绘制的最大宽度是可选的.