本篇為小程序的一學(xué)習(xí)筆記竣稽,以一小游戲‘層疊消融’為例子囱怕,對(duì)canvas組件進(jìn)行展開學(xué)習(xí)霍弹。
開場(chǎng)白就沒了,直奔主題娃弓。先來看看小程序在組件使用上典格,對(duì)canvas做了什么。
em……台丛,首先钝计,指定了作為唯一標(biāo)識(shí)符的屬性canvas-id,因?yàn)樾〕绦虼盍艘惶譓V*框架齐佳,所以對(duì)于元素綁定這個(gè)私恬,和用原生的js的方式不同。然后就是一系列的手勢(shì)觸發(fā)事件炼吴。其他的本鸣,其實(shí)在使用上也沒做什么特別的處理嘛。既然如此硅蹦,那就直接貼上對(duì)canvas進(jìn)行元素綁定的代碼吧荣德。
<!-- canvas.wxml -->
<canvas canvas-id="testCanvas"></canvas>
<!-- canvas.js -->
Page({
????onReady: function(e) {
????????// 使用wx.createContext獲取繪圖上下文 context
????????var context = wx.createCanvasContext('testCanvas');
????????context.setStrokeStyle('#00ff00');
????????context.rect(0,0,200,200);
????????context.draw();
????}
})
因?yàn)樾〕绦虻腸anvas元素默認(rèn)寬高是不會(huì)覆蓋全屏的,所以在初始化canvas之后要進(jìn)行重新設(shè)置寬高童芹。屏幕的寬高可以通過API:wx.getSystemInfo獲得涮瞻。
接下來就是畫圖了〖偻剩‘層疊消融’這個(gè)游戲的主要效果就是圖片疊加部分雙數(shù)相消署咽。具體效果如圖:
上面的圖形由三個(gè)基本圖形(正方形)組成,用這三個(gè)基本圖形拼出上面的目標(biāo)圖案生音,則算通關(guān)宁否。
需求:多圖形相疊雙數(shù)相消。要實(shí)現(xiàn)這個(gè)效果缀遍,用傳統(tǒng)的css是很麻煩的慕匠,這時(shí)canvas的靈活性就體現(xiàn)出來了。canvas對(duì)圖片圖形的處理是像素級(jí)的域醇,其本身提供了一系列的圖形遮蓋策略台谊。globalCompositeOperation這個(gè)屬性設(shè)定了在畫新圖形時(shí)采用的遮蓋策略,其值是一個(gè)標(biāo)識(shí)12種遮蓋方式的字符串譬挚。其中‘xor’方式正能實(shí)現(xiàn)我們雙數(shù)相消的需求锅铅。
立即貼上主要的代碼:
//繪制一個(gè)反相圖案
let revert = function(path){
????this.context.beginPath();
????this.context.fillStyle="#000";
????this.context.globalCompositeOperation="xor";
????for(let i=0;i<path.length;i++){
????????if(i==0){
????????????this.context.moveTo(path[i].x,path[i].y);
????????}else{
????????????this.context.lineTo(path[i].x,path[i].y);
????????}
????}
????this.context.fill();
????return this;
}
(小程序的onReady函數(shù),這里具體的調(diào)用我打包了一下殴瘦,具體打包細(xì)節(jié)我就不再這里細(xì)講了)
onReady: function(){
????let canvasCtl = new this.canvasCtl();
????canvasCtl.init(this);
????let block1 = new util.block([{x:200,y:50},{x:100,y:150},{x:100,y:50}],canvasCtl);
????block1.initBlock();
????let block2 = new util.block([{x:150,y:70},{x:250,y:70},{x:250,y:200}],canvasCtl);
????block2.initBlock();
????let block3 = new util.block([{x:50,y:60},{x:110,y:60},{x:110,y:110},{x:50,y:110}],canvasCtl);
????block3.initBlock();
????this.blockList.push(block1);
????this.blockList.push(block2);
????this.blockList.push(block3);
},
效果圖如下:
既然是小游戲狠角,那就要會(huì)動(dòng)是不是号杠。需求:手指點(diǎn)上時(shí)蚪腋,選中圖片跟著移動(dòng)丰歌。
這時(shí),就要用上小程序給我們提供的手勢(shì)觸發(fā)了屉凯,用到的有這兩個(gè):
bindtouchstart用于判斷手指選中那個(gè)圖形立帖,bindtouchmove用于圖形移動(dòng)。
對(duì)于判定選中圖形這個(gè)問題悠砚,趕緊回憶起你的小學(xué)數(shù)學(xué):沿著判定點(diǎn)向圖形方向畫一條射線晓勇,若相交點(diǎn)為單數(shù),則判定點(diǎn)在圖形中灌旧,否則绑咱,判定點(diǎn)在圖形外。轉(zhuǎn)換為代碼模式如下:
block.prototype.checkPointInRegion = function(pt){
????let nCross = 0; // 定義變量枢泰,統(tǒng)計(jì)目標(biāo)點(diǎn)向右畫射線與多邊形相交次數(shù)
????for (let i = 0; i < this.path.length; i++) { //遍歷多邊形每一個(gè)節(jié)點(diǎn)
????????let p1 = this.path[i];
????????let p2 = this.path[(i+1)%this.path.length];
????????// p1是這個(gè)節(jié)點(diǎn)描融,p2是下一個(gè)節(jié)點(diǎn),兩點(diǎn)連線是多邊形的一條邊
????????// 以下算法是用是先以y軸坐標(biāo)來判斷的
????????if ( p1.y == p2.y )continue;//如果這條邊是水平的衡蚂,跳過
????????if ( pt.y < ((p1.y= ((p1.y>p2.y)?p1.y:p2.y))continue;//如果目標(biāo)點(diǎn)高于這個(gè)線段窿克,跳過
????????//那么下面的情況就是:如果過p1畫水平線,過p2畫水平線毛甲,目標(biāo)點(diǎn)在這兩條線中間
????????let x = (pt.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
????????// 這段的幾何意義是 過目標(biāo)點(diǎn)年叮,畫一條水平線,x是這條線與多邊形當(dāng)前邊的交點(diǎn)x坐標(biāo)
????????if ( x > pt.x ) nCross++; //如果交點(diǎn)在右邊玻募,統(tǒng)計(jì)加一只损。這等于從目標(biāo)點(diǎn)向右發(fā)一條射線(ray),與多邊形各邊的相交(crossing)次數(shù)
????}
????if (nCross % 2 == 1) {
????????return true; //如果是奇數(shù)七咧,說明在多邊形里
????} else {
????????return false; //否則在多邊形外 或 邊上
????}
}
下面就是移動(dòng)了改执,canvas的圖形的移動(dòng)很粗暴,那就是擦掉舊的重新畫上新的坑雅。需要記錄下多次bindtouchmove觸發(fā)時(shí)手指的位移->計(jì)算出圖形移動(dòng)后的新定點(diǎn)坐標(biāo)->在畫布上擦掉舊圖形->根據(jù)新坐標(biāo)畫上新圖形辈挂。下面貼上主要代碼:
searchBlock:function(e){?
????if(this.movePath.length>10){?
?????????this.movePath.shift();?
?????}
?????this.movePath.push(e);
? ??for(let i = 0; i < this.blockList.length; i++){//讓新選中的圖形永遠(yuǎn)處于第一位
? ? ? if(this.blockList[i].checkPointInRegion({ x: e.touches[0].x , y:e.touches[0].y })){
? ? ? ? [this.blockList[0],this.blockList[i]] = [this.blockList[i],this.blockList[0]]
? ? ? };
? ? }
? },?
?moveBlock:function(e){
? ? if(this.movePath.length>10){
? ? ? this.movePath.shift();
? ? }
? ? this.movePath.push(e);
? ? let oldPath = this.movePath[this.movePath.length-2]?this.movePath[this.movePath.length-2]:null;
? ? let newPath = e;
? ? this.blockList[0].move(oldPath.touches[0],newPath.touches[0]);
? },
block.prototype.move = function(oldPath,newPath){
? ? if(oldPath){
? ? ? var x = newPath.x - oldPath.x;
? ? ? var y = newPath.y - oldPath.y;
? ? ? this.changeLocation(x,y);
? ? }
}
效果圖如下:
接下去就是通關(guān)判定、關(guān)卡設(shè)置裹粤、關(guān)卡選擇……有點(diǎn)多终蒂,這些內(nèi)容就留到下期吧。