1.预备知识
(1)Canvas绘制多边形
(2)Canvas绘制文字
2.实现思路
涉及的对象
(1)场景Scene
场景代表了画布上的一块区域,场景里的每个物体都是场景里的一个元素,其绘制统一由场景来调用绘制
(2) 扑克牌Card
包括翻开,关闭,移除等操作
(3)一副扑克牌Deck
包括洗牌
(4)游戏玩法PlayingRule
每次选择2张扑克进行比较,相等则消除(移除),不相等,则进行下一次的2张牌的选择 ,在进行比较
3.主要代码
/*场景*/ function Scene(canvasId){ var canvas = document.getElementById(canvasId) var ctx = canvas.getContext('2d') var width = canvas.width var height = canvas.height //场景里所有的元素 var elements = [] function initEvents(){ canvas.addEventListener('click',function(e){ for(var i=0;i<elements.length;i++){ if( typeof elements[i].onEvent == 'function' ){ elements[i].onEvent(e) } } },false) } return { addElement : function(element){ elements.push(element) }, init : function(){ for(var i=0;i<elements.length;i++){ if( typeof elements[i].init == 'function' ){ elements[i].init(ctx) } } initEvents() } } } /* 扑克牌 德国扑克牌(一付55/54/32张)规格:9x5.7cm 8.7x5.7cm */ function Card(x,y,value,index){ this.x = x this.y = y this.value = value this.index = index this.status = this.StatusType.Closed this.ctx = null } Card.prototype = { constructor : Card, width : 60, height : 90, ratio : 1, StatusType : {Closed:0,Opened:1,Removed:2}, timeout : 300, init : function(ctx){ this.ctx = ctx this.draw() }, draw : function(){ var ctx = this.ctx ctx.clearRect(this.x,this.y,this.width,this.height) switch(this.status){ case 2 : ctx.fillStyle = '#ccc' ctx.fillRect(this.x,this.y,this.width*this.ratio,this.height*this.ratio) break break case 1 : ctx.fillStyle = '#36f' ctx.fillRect(this.x,this.y,this.width*this.ratio,this.height*this.ratio) //添加文字 ctx.fillStyle = '#fff' ctx.font = 'bold 50px Verdana' ctx.fillText(this.value,this.x+this.width*.5-17,this.y+this.height*.5+17) break case 0 : default : ctx.fillStyle = '#f60' ctx.fillRect(this.x,this.y,this.width*this.ratio,this.height*this.ratio) break } }, onEvent : function(e,cardPicked){ var x = e.offsetX, y = e.offsetY //牌被选中,显示该牌的value值 if(this.x<=x&&x<=this.x+this.width&&this.y<=y&&y<=this.y+this.height){ this.open()//翻开扑克 var self = this cardPicked(self) setTimeout(function(){ self.close()//关闭扑克牌 },self.timeout) } cardPicked(null) }, open : function(){ if(this.status == this.StatusType.Closed){ this.status = this.StatusType.Opened this.draw() } }, close : function(){ if(this.status == this.StatusType.Opened){ this.status = this.StatusType.Closed this.draw() } }, remove : function(){ this.status = this.StatusType.Removed this.draw() } } /*整付扑克牌 52张*/ function Deck(){ this.cards = [] this.rule = new PlayingRule() } Deck.prototype = { constructor : Deck, opts : {}, //cards : 52, margin : 10,//牌之间的间距 init : function(ctx){ this.opts.ctx = ctx this.draw() this.shuffle() }, draw : function(){ var ctx = this.opts.ctx var x = this.margin, y = this.margin, i = 3, index = -1 for(i=3;i<9;i++){ var card1 = new Card(x,y,i,++index) var card2 = new Card(x,y+card1.height+this.margin,i,++index) card1.init(ctx) card2.init(ctx) //console.log(card1.x+','+card1.y) //console.log(card2.x+','+card2.y) this.cards.push(card1) this.cards.push(card2) x += card1.width+this.margin y = y } }, //洗牌 shuffle : function(){ var length = this.cards.length var r1 var r2 var i var value for(i=0;i<length;i++){ r1 = Math.floor(Math.random()*length) r2 = Math.floor(Math.random()*length) value = this.cards[r1].value this.cards[r1].value = this.cards[r2].value this.cards[r2].value = value } /*debug*/ for(i=0;i<length;i++){ console.log(this.cards[i].index+' : '+this.cards[i].value) } }, onEvent : function(e){ var cards = this.cards var length = cards.length var i var card var self = this for(i=0;i<length;i++){ card = cards[i] if(typeof card.onEvent == 'function'){ card.onEvent(e,function(card){ if(card==null||card.status==card.StatusType.Removed){ return } //有扑克牌被选中 var isMatch = self.rule.pushCard(card).isMatch() var cards = self.rule.getCards() var length = cards.length if(isMatch){ for(var i=0;i<length;i++){ var card = cards[i] alert(card.index) card.remove() } } else{ //如果不匹配,当前选中的扑克牌会自动定时关闭 //DONOTHING } //如果有2张扑克,则清除玩法中的选中的2张扑克牌 self.rule.refresh() }) } } } } /*玩法规则*/ function PlayingRule(){ var cards = [] return { pushCard : function(card){ if(cards.length<2){ cards.push(card) console.log('push:'+card.value) } return this }, isMatch : function(){ if(cards.length==2){ return cards[0].value === cards[1].value } return false }, refresh : function(){ if(cards.length==2){ cards.length = 0 } }, getCards : function(){ return cards; } } } //app window.onload = function(){ var scene = new Scene('canvas1') var deck = new Deck() scene.addElement(deck) scene.init() }
4.优化和完善
(1)有bug,可以作弊,选择的拥有比较的2张牌可以为同一张,需添加判断
(2)简化了书中的例子,图形图片绘制改为了数字文字的现实
(3)可以进行图形绘制,使扑克牌逼真些
(4)考虑自适应大小的设置,如扑克牌的比例随着手机屏幕大小自适应