用Canvas实现刮刮卡功能的研究与实践

    前几天,单位老刘让我研究一下网上最近很流行的“刮刮卡”是怎么实现的,我第一时间就想起了我媳妇曾经跟我说过的:“在HTML5中Canvas的绘图能力很强大”这句话。所以,我也没多加思考就上网搜集了一些关于HTML5的书籍和资料。为了赶进度,在匆匆阅读之后就开始动手做了起来,再大干了1天之后,终于用“双Canvas技巧”实现了一个蹩脚的刮刮卡功能,难用程度就连自己都觉得非常差劲。

    早上在给老刘看了之后,老刘问了我一些问题,然后就找了一篇资料让我再看一下。研究过后,我发现原来自己由于对HTML5的特性不甚理解,在设计实现的思路上犯了错误。为了完成任务,更为了满足自己的探索欲望,便结合着文章中的示例与自己的一些想法,重新做了一个刮刮卡的实现,这次的实现还算是令我满意,感觉和淘宝在双十一时使用过的刮刮卡差不多了。

下面我就来简单介绍一下我的实现思路:

1、示例中用到的(部分)重要的html5 API介绍

document.querySelector('canvas');
得到页面中canvas元素。
canvas.getContext('2d');
得到绘图的上下文。
ctx.fillStyle = 'transparent';
设置矩形填充的属性,主要为矩形填充方法服务。
ctx.fillRect(0, 0, w, h);
根据给定的坐标与大小绘制一个矩形。
ctx.globalCompositeOperation = 'destination-out';(关键属性)
设置前后绘制的图形组合显示的效果。
ctx.getImageData(0, 10, w, h);
得到图片中的像素数据。
ctx.clearRect(0, 0, w, h);
根据给定的“坐标和大小”,以矩形方式擦掉原图层。
ctx.beginPath();
开始一个新的绘制路径。
ctx.arc(x, y, 25, 0, Math.PI * 2);
根据给定的“坐标,弧度与半径”绘制一条弧线。
ctx.;fill();
绘制后填充。

2、实现思路概述

  1. 在后台根据“用户等级、中奖几率以及其他条件”计算出该用户此次抽奖是否可以抽中,并把计算出来的结果传递给前台页面;
  2. 前台页面再得到了计算结果后,用JS创建一个与计算结果“相符”的“中奖图片”对象;
  3. 设置Canvas的背景为该“中奖图片”;
  4. 在Canvas上绘制一个与“中奖图片”同样大小的“灰色覆盖区域”,以遮挡住图片信息;
  5. 在“灰色覆盖区域”上继续绘制之前,先设置一下图层的叠加方式(设置为:“在已有内容和新图形不重叠的地方,已有内容保留,所有其他内容成为透明。”),用以模拟擦除的效果;
  6. 在“灰色覆盖区域”上,使用算法绘制一条用以模拟手指的擦除效果的弧线;
  7. 当用户“擦除”了一定比例的遮挡区域后,就通知用户是否中奖,同时清除所有的“灰色覆盖区域”,让“中奖图片”完整的显示给用户看,以提升可用性。
3、代码实现

<jsp:directive.page contentType="text/html; charset=UTF-8" language="java" />
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<jsp:directive.page session="false" />
<c:set var="ctxPath" value="${pageContext.request.contextPath}" />

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
		<style type="text/css">
			html,body {
				width: 100%;
				height: 100%;
				margin: 0;
				padding: 0;
			}
			
			body {
				moz-user-select: none;
				webkit-user-select: none;
			}
			
			div,select,input,p,ul,li {
				margin: 0;
				padding: 0;
			}
			
			body {
				font-size: 12px;
				font-family: "宋体";
				line-height: 2em;
			}
			
			.main {
				width: 100%;
				height: 100%;
				overflow: hidden; hidden;
				margin: 0;
			}
		</style>
		<title>单canvas实现刮刮卡</title>
	</head>

	<body>
		<div id="container" class="main">
			<canvas/>
		</div>
	</body>
	
	<script type="text/javascript">
		
		(function (bodyStyle) {
			
			// 在FF浏览器环境中,通过修改CSS3属性,使子元素中所有的文字都不能选择。
	        bodyStyle.mozUserSelect = 'none';
	        // 在Google浏览器环境中,通过修改CSS3属性,使子元素中所有的文字都不能选择。
	        bodyStyle.webkitUserSelect = 'none';
	        
	        // 得到canvas对象。
	        var canvas = document.querySelector('canvas');
	        var canvasStyle = canvas.style;
	        // 设置canvas的定位方式。
	        canvasStyle.position = 'absolute';
	        // 设置canvas的背景颜色为“透明”。
	        canvasStyle.backgroundColor='transparent';
	        
	        // 构造一个图片对象。
			var img = new Image();
			img.src = '${ctxPath}/image/guagua_1canvas.png';
			
			// 监听载图片加载事件。
	        img.addEventListener('load', function(e) {
	        	
	            var w = img.width,
	                h = img.height;
	            var mousedown = false;
	            
	        	// 把canvas的“宽”和“高”设置为图片的宽和高。
	            canvas.width = w;
	            canvas.height = h;
	            
	            // 把canvas移动到屏幕中央。
	        	canvas.style.left = ((document.body.clientWidth - w) / 2) + "px";
	            canvas.style.top = ((document.body.clientHeight - h) / 2) + "px";
	            
	            // 把canvas绘布的背景设置为中奖图片。
	            canvas.style.backgroundImage='url('+img.src+')';
	
	            // 得到绘图的上下文,且绘制一个与中将图片同样大小的透明层。
	            var ctx = canvas.getContext('2d');
	            ctx.fillStyle = 'transparent';
	            ctx.fillRect(0, 0, w, h);
	            
	            // 使用“gray”颜色对canvas画布进行“矩形”填充。
	            ctx.fillStyle = 'gray';
	            ctx.fillRect(0, 0, w, h);
	 
	            // 在已有内容和新图形不重叠的地方,已有内容保留,所有其他内容成为透明。
	            ctx.globalCompositeOperation = 'destination-out';
	            
	            // 处理“鼠标”或“手指”按下时的动作。
	            function eventDown(e){
	                // 通知浏览器不要执行与事件关联的默认动作。
	                e.preventDefault();
	                mousedown = true;
	            }
	
	            // 处理“鼠标”或“手指”按下后移动时的动作。
	            function eventMove(e){
	                e.preventDefault();
	                if (mousedown) {
	                    // 如果存在涉及当前事件的手指的一个列表(这里是指正在移动中的手指)。
	                    if (e.changedTouches){
	                        // 取得涉及当前事件中众多手指中的最后一个。
	                        e = e.changedTouches[e.changedTouches.length - 1];
	                    }
	                    
	                    // 计算当前“鼠标”或“手指”在canvas中的坐标(注意,计算的坐标是canvas里的坐标)。
	                    var x = (e.clientX + document.body.scrollLeft || e.pageX) - canvas.offsetLeft || 0,
	                        y = (e.clientY + document.body.scrollTop || e.pageY) - canvas.offsetTop || 0;
	
	                    with (ctx) {
	                        beginPath();
	                        arc(x, y, 25, 0, Math.PI * 2);
	                        fill();
	                    }
	                }
	            }
	            
	            // 处理“鼠标”或“手指”按下后抬起时的动作。
	            function eventUp(e){
	                e.preventDefault();
	                mousedown = false;
	                // 得到中奖图片的像素数据(像素计算非常耗费CPU和内存,可能会导致浏览器崩溃)。
					var data = ctx.getImageData(0, 10, w, h).data;
	                // 通过计算每一个像素,得知还有多少“遮挡区域”。
					for (var i = 0, j = 0; i < data.length; i += 4) {
					    if (data[i] && data[i + 1] && data[i + 2] && data[i + 3]) {
					        j++;
					    }
					}
	                
	                // 当还只剩下20%的遮挡区域时弹出中奖信息,同时撤掉遮挡区域。
					if (j <= w * h * 0.35) {
					    alert('恭喜您中了2元现金');
			            ctx.clearRect(0, 0, w, h);
					}
	            }
	            
	            // 监听“触摸的开始、移动与抬起”事件。
	            canvas.addEventListener('touchstart', eventDown);
	            canvas.addEventListener('touchend', eventUp);
	            canvas.addEventListener('touchmove', eventMove);
	            
	            // 监听“鼠标的开始、移动与抬起”事件。
	            canvas.addEventListener('mousedown', eventDown);
	            canvas.addEventListener('mouseup', eventUp);
	            canvas.addEventListener('mousemove', eventMove);
	        });
        
		})(document.body.style);
        
	</script>

</html>


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值