微信小程序“别踩白块”源码分享
一、项目目录框架及配置
1.文件框架
(1)pages文件夹:
(2)app.js
(3)app.json
(4)app.wxss
(5)app.wxml
(6)project.config.json
(7)sitemap.json
(8)utills文件夹
2.项目配置
二、别踩白块结构分析
1. 模块功能:
2. 文件配置
二、别踩白块各模块代码
1. 全局配置
(1)app.json
(2)app.js
(3)app.wxss
(4)project.config.json
2.首页配置(index)
(1)index.js
(2)index.json
(3)index.wxml
(4)index.wxss
3. 无尽模式配置(endless)
(1)play.js
(2)play.json
(3)play.wxml
(4)play.wxss
4. 计时模式配置(time)
(1)play.js
(2)play.json
(3)play.wxml
(4)play.wxss
5. 急速模式配置(speed)
(1)play.js
(2)play.json
(3)play.wxml
(4)play.wxss
6. 游戏结束配置(end)
(1)end.js
(2)end.json
(3)end.wxml
(4)end.wxss
7. 日志文件配置(logs)
(1)logs.js
(2)logs.json
(3)logs.wxml
(4)logs.wxss
三、别踩白块效果实现
1. 首页效果
2. 模块效果
先看效果图(自己做小程序很简单,下面是具体实现)
首先,要了解微信小程序的开发工具——微信开发者工具
可以在微信公众平台下载软件
一、项目目录框架及配置
1.文件框架
(1)pages文件夹:
存放小程序的页面文件,书写各个页面代码以及组件,里面包括各种全局文件。
(2)app.js
小程序入口文件,用于定义全局数据和函数的使用,可以指定微信小程序的生命周期函数。
(3)app.json
全局文件, 对小程序进行配置,小程序的全局配置,小程序的所有页面路径、界面表现、网络超时时间、底部 tab 等; 包括配置小程序是由哪些页面组成,配置小程序的窗口及背景色,配置导航条样式,配置默认标题等。
(4)app.wxss
全局的样式文件。
(5)app.wxml
构建页面结构
(6)project.config.json
保存开发工具配置项
(7)sitemap.json
网站地图,可以对小程序进行seo优化,让搜索排名靠前
(8)utills文件夹
存放全局的一些.js文件,公共用到的一些事件处理代码文件可以放到该文件夹下,用于全局调用。
以下是结构示图
2.项目配置
一个小程序最开始要使用app.json文件来对微信小程序进行全局配置,它决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等基础元素。
{ "pages": [ "pages/index/index", "pages/endless/play", "pages/time/play", "pages/speed/play", "pages/end/end", "pages/logs/logs" ], "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "别踩白块", "navigationBarTextStyle": "black" }, "sitemapLocation": "sitemap.json" }
二、别踩白块结构分析
1. 模块功能:
无尽模式、计时模式、急速模式、查看日志
2. 文件配置
二、别踩白块各模块代码
1. 全局配置
(1)app.json
代码如下:
{ "pages": [ "pages/index/index", "pages/endless/play", "pages/time/play", "pages/speed/play", "pages/end/end", "pages/logs/logs" ], "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "别踩白块", "navigationBarTextStyle": "black" }, "sitemapLocation": "sitemap.json" }
(2)app.js
代码如下:
//app.js App({ onLaunch: function () { //调用API从本地缓存中获取数据 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) }, getUserInfo:function(cb){ var that = this if(this.globalData.userInfo){ typeof cb == "function" && cb(this.globalData.userInfo) }else{ //调用登录接口 wx.login({ success: function () { wx.getUserInfo({ success: function (res) { that.globalData.userInfo = res.userInfo typeof cb == "function" && cb(that.globalData.userInfo) } }) } }) } }, getHeighestScore: function(scoreName, cb){ this.globalData[scoreName] = wx.getStorageSync(scoreName); typeof cb == "function" && cb(this.globalData[scoreName]) }, globalData:{ userInfo: null, currentScore: 0, endlessScore: null, timeScore: null, speedScore: null } })
(3)app.wxss
代码如下:
/**app.wxss**/ .container { height: 100%; display: flex; flex-direction: column; justify-content: center; box-sizing: border-box; }
(4)project.config.json
代码如下:
{ "description": "项目配置文件", "packOptions": { "ignore": [] }, "setting": { "urlCheck": true, "es6": true, "enhance": false, "postcss": true, "preloadBackgroundData": false, "minified": true, "newFeature": false, "coverView": true, "nodeModules": false, "autoAudits": false, "showShadowRootInWxmlPanel": true, "scopeDataCheck": false, "uglifyFileName": false, "checkInvalidKey": true, "checkSiteMap": true, "uploadWithSourceMap": true, "compileHotReLoad": false, "useMultiFrameRuntime": true, "useApiHook": true, "useApiHostProcess": false, "babelSetting": { "ignore": [], "disablePlugins": [], "outputPath": "" }, "enableEngineNative": false, "bundle": false, "useIsolateContext": true, "useCompilerModule": true, "userConfirmedUseCompilerModuleSwitch": false, "userConfirmedBundleSwitch": false, "packNpmManually": false, "packNpmRelationList": [], "minifyWXSS": true }, "compileType": "miniprogram", "libVersion": "2.16.1", "appid": "wxd5512791eaec8564", "projectname": "miniprogram-1", "debugOptions": { "hidedInDevtools": [] }, "scripts": {}, "isGameTourist": false, "condition": { "search": { "list": [] }, "conversation": { "list": [] }, "game": { "list": [] }, "plugin": { "list": [] }, "gamePlugin": { "list": [] }, "miniprogram": { "list": [] } } }
2.首页配置(index)
(1)index.js
代码如下:
//index.js //获取应用实例 var app = getApp() Page({ data: { userInfo: {}, heighestScore: 0, longestTime: 0 }, //事件处理函数 bindViewTap: function() { wx.navigateTo({ url: '../logs/logs' }) }, goGame: function(event){ var gameType = event.target.id; wx.redirectTo({ url: '../'+gameType+'/play' }) }, onLoad: function () { var that = this //调用应用实例的方法获取全局数据 app.getUserInfo(function(userInfo){ //更新数据 that.setData({ userInfo:userInfo }) }) // 最高分数 app.getHeighestScore('endlessScore', function(heighestScore){ app.globalData.endlessScore = heighestScore || 0; that.setData({ heighestScore: heighestScore || 0 }) }); // 最长时间 app.getHeighestScore('timeScore', function(heighestScore){ app.globalData.timeScore = heighestScore || 0; that.setData({ longestTime: heighestScore || 0 }) }); } })
(2)index.json
代码如下:
{}
(3)index.wxml
代码如下:
<!--index.wxml--> <view class="container"> <view class="line"> <view class="blocks black" id="endless" bindtap="goGame"> <text>无尽模式</text> </view> <view class="blocks" id="time" bindtap="goGame"> <text>计时模式</text> </view> </view> <view class="line"> <view class="blocks" id="speed" bindtap="goGame"> <text>急速模式</text> </view> <view class="blocks black" bindtap="bindViewTap"> <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image> <text class="userinfo-nickname">{{userInfo.nickName}}</text> </view> </view> <view class="line"> <view class="blocks black"> <text>最高分数:{{heighestScore}}</text> </view> <view class="blocks"> <text>最长时间:{{longestTime}}</text> </view> </view> </view>
(4)index.wxss
代码如下:
/**index.wxss**/ page { height: 100%; } .line { height: 33%; display: flex; flex-direction: row; justify-content: center; } .blocks { width: 50%; display: flex; flex-direction: column; justify-content: center; align-items: center; box-sizing: border-box; } .black { background: #000000; } .black text { color: #ffffff; } .userinfo { display: flex; flex-direction: column; align-items: center; } .userinfo-avatar { width: 128rpx; height: 128rpx; margin: 20rpx; border-radius: 50%; } .userinfo-nickname { color: #aaa; }
3. 无尽模式配置(endless)
(1)play.js
代码如下:
// play var app = getApp() Page({ data: { typeName: '无尽模式', silding: false, score: 0, blockData:[] }, onReady: function(){ var array = []; // 先生成一个10个长度的数组 for(var i = 0; i < 10; i++){ // 生成一个随机位数为1的数组 var orderArray = [0,0,0,0]; var randomNum = Math.floor(Math.random() * 4); orderArray[randomNum] = 1; array.push({id: i, block: orderArray}); } this.setData({ blockData: array.reverse() }); }, handleClick: function(events){ var id = events.currentTarget.id; var line = id.split("-")[1]; var column = id.split("-")[2]; var isBlack = id.split("-")[3]; var blockData = this.data.blockData.reverse(); var score = this.data.score; var orderArray = [0,0,0,0]; // 判断是否是第一行 if(line != blockData[0].id){ this.handleWrong(0, score); return; } // 判断是否正确 if(isBlack != 1){ this.handleWrong(1, score); return; } // 正确下一个 // 分数++ // 最后一个小块的id为分数+10 score++; orderArray[Math.floor(Math.random() * 4)] = 1; blockData.push({id: score+10, block: orderArray}); blockData.shift(); this.setData({ silding: true, score: score, blockData: blockData.reverse() }); }, handleWrong: function( type , score){ const titleArr = ["请点击第一个白块!游戏结束", "别点白块!游戏结束"]; wx.showToast({ title: titleArr[type], icon: 'cancel', duration: 2000, complete: function(){ // 将此分数存入全局变量 app.globalData.currentScore = score; // 若此分数比最高分数还高 将其存入本地 if(score > app.globalData.endlessScore){ app.globalData.endlessScore = score; wx.setStorageSync('endlessScore',score); } var timer = setTimeout(function(){ wx.redirectTo({ url: '../end/end?type=endless&score=' + score }) clearTimeout(timer); }, 2000); } }) }, onLoad: function(){ var that = this; wx.setNavigationBarTitle({ title: that.data.typeName }); } })
(2)play.json
游戏模式中不需要全局配置
代码如下:
{}
(3)play.wxml
代码如下:
<!--play.wxml--> <view class="score">{{score}}</view> <view class="play-box"> <block wx:for="{{blockData}}" wx:for-index="i" wx:key="i"> <view class="block-line" id="line-{{blockData[i].id}}"> <block wx:for="{{blockData[i].block}}" wx:key="*this" wx:for-index="j"> <view wx:if="{{blockData[i].block[j] == 0}}" id="block-{{blockData[i].id}}-{{j}}-{{0}}" class="block" bindtap="handleClick"></view> <view wx:else class="block black" id="block-{{blockData[i].id}}-{{j}}-{{1}}" bindtap="handleClick"></view> </block> </view> </block> </view>
(4)play.wxss
代码如下:
/*page { display: flex; flex-direction: column-reverse; }*/ .score { position: fixed; top: 2rem; left: 50%; font-size: 3rem; color: #fc2736; transform: translate(-50%, 0); z-index: 10; } .play-box { width: 100%; position: fixed; left: 0; bottom: 0; } .block-line { width: 100%; overflow: hidden; } .block { width: 5rem; height: 8rem; float: left; border: 1px solid #000000; box-sizing: border-box; } .black { background: #000000; }
4. 计时模式配置(time)
(1)play.js
代码如下:
// play var app = getApp() Page({ data: { typeName: '计时模式', score: 0, time: 60, shouldStop: false, blockData:[] }, onReady: function(){ var array = []; // 先生成一个10个长度的数组 for(var i = 0; i < 10; i++){ // 生成一个随机位数为1的数组 var orderArray = [0,0,0,0]; var randomNum = Math.floor(Math.random() * 4); orderArray[randomNum] = 1; array.push({id: i, block: orderArray}); } this.setData({ blockData: array.reverse() }); }, handleClick: function(events){ var id = events.currentTarget.id; var line = id.split("-")[1]; var column = id.split("-")[2]; var isBlack = id.split("-")[3]; var blockData = this.data.blockData.reverse(); var score = this.data.score; var orderArray = [0,0,0,0]; // 判断是否是第一行 if(line != blockData[0].id){ this.handleWrong(0, score); return; } // 判断是否正确 if(isBlack != 1){ this.handleWrong(1, score); return; } // 正确下一个 // 分数++ // 最后一个小块的id为分数+10 score++; orderArray[Math.floor(Math.random() * 4)] = 1; blockData.push({id: score+10, block: orderArray}); blockData.shift(); this.setData({ silding: true, score: score, blockData: blockData.reverse() }); }, handleWrong: function( type , score){ const titleArr = ["请点击第一个白块!游戏结束", "别点白块!游戏结束", "时间到"]; var _this = this; wx.showToast({ title: titleArr[type], icon: 'cancel', duration: 2000, complete: function(){ // 将此分数存入全局变量 app.globalData.currentScore = score; // 停止计数器 _this.setData({ shouldStop: true }); // 若此分数比最高分数还高 将其存入本地 if(score > app.globalData.timeScore){ app.globalData.timeScore = score; wx.setStorageSync('timeScore',score); } var timer = setTimeout(function(){ wx.redirectTo({ url: '../end/end?type=time&score=' + score }) clearTimeout(timer); }, 2000); } }) }, timeInterval: function(){ var that = this; var timer = setInterval(function(){ // 判断是否小于0 var nowTime = that.data.time; if(that.data.shouldStop){ clearInterval(timer); } if(nowTime > 1){ that.setData({ time: nowTime-1 }); return; } that.setData({ time: nowTime-1 }); that.handleWrong(2, that.data.score); clearInterval(timer); }, 1000); }, onLoad: function(){ var that = this; wx.setNavigationBarTitle({ title: that.data.typeName }); this.timeInterval(); } })
(2)play.json
代码如下:
{}
(3)play.wxml
代码如下:
<!--play.wxml--> <view class="time">时间:{{time}}s</view> <view class="score">分数:{{score}}</view> <view class="play-box"> <block wx:for="{{blockData}}" wx:for-index="i" wx:key="i"> <view class="block-line" id="line-{{blockData[i].id}}"> <block wx:for="{{blockData[i].block}}" wx:key="*this" wx:for-index="j"> <view wx:if="{{blockData[i].block[j] == 0}}" id="block-{{blockData[i].id}}-{{j}}-{{0}}" class="block" bindtap="handleClick"></view> <view wx:else class="block black" id="block-{{blockData[i].id}}-{{j}}-{{1}}" bindtap="handleClick"></view> </block> </view> </block> </view>
(4)play.wxss
代码如下:
/* pages/time/play.wxss */ .time { position: fixed; top: 2rem; left: 50%; font-size: 2rem; color: #fc2736; transform: translate(-50%, 0); z-index: 10; } .score { position: fixed; top: 5rem; left: 50%; font-size: 2rem; color: #fc2736; transform: translate(-50%, 0); z-index: 10; } .play-box { width: 100%; position: fixed; left: 0; bottom: 0; } .block-line { width: 100%; overflow: hidden; } .block { width: 5rem; height: 8rem; float: left; border: 1px solid #000000; box-sizing: border-box; } .black { background: #000000; }
5. 急速模式配置(speed)
(1)play.js
代码如下:
// // play var app = getApp() Page({ data: { typeName: '急速模式', score: 0, blockData:[], scrollHeight: 0, canRun: false }, onLoad: function(){ var that = this; // 设置title wx.setNavigationBarTitle({ title: that.data.typeName }); }, onReady: function(){ var array = []; // 先生成一个10个长度的数组 for(var i = 0; i < 10; i++){ array.push(this.getNewLine(i)); } this.setData({ blockData: array.reverse() }); }, handleClickWhite: function(events){ // 点击白块一定会报错 差别在于报错文案 // 判断是否是点击的第一行 // 被点击的id var id = events.currentTarget.id; // 被点击的行 var line = id.split("-")[1]; // 数据 var blockData = this.data.blockData.concat().reverse(); // 当前分数 var score = this.data.score; // 判断是否是第一行 if(line != this.getClickableBlockLine(blockData)){ this.handleWrong("请点击第一个黑块!游戏结束", score); } else { // 点击的第一行白块 this.handleWrong("别点白块!游戏结束", score); } }, handleClickBlack: function(events){ // 黑块是应该点击的块 // 判断是否是点击的第一行 // 被点击的id var id = events.currentTarget.id; // 被点击的行 var line = id.split("-")[1]; // 数据 var blockData = this.data.blockData.concat().reverse(); // 当前分数 var score = this.data.score; // 可点击的第一行 var clickableLine = this.getClickableBlockLine(blockData); // 判断是否是第一行 if(line == clickableLine){ // 点击了第一行黑块 // 判断是否是是第一次 if(score == 0){ // 启动滑动程序 this.run(); } score++; // 将黑块变灰块 this.getBlockBlackToGray(line, blockData); // 分数++ this.setData({ score: score, blockData: blockData.concat().reverse() }); } else { // 点击的不是第一行白块 this.handleWrong("请点击第一个黑块!游戏结束", score); } }, handleClickGray: function(events){ // 灰块是指黑块点击之后的块 // 其在显示是白块 并且同样不可点 var score = this.data.score; this.handleWrong("别点白块!游戏结束", score); }, run: function(){ // 滑动方法 var that = this; var speed = 10; this.setData({ canRun: true }); var timer = setInterval(function(){ // 当前滑动距离 if(!that.data.canRun){ clearInterval(timer); return; } var currentScrollHeight = that.data.scrollHeight; // 当前分数 var score = that.data.score; // 滑块数据 var blockData = that.data.blockData.concat().reverse(); if(Math.abs(currentScrollHeight) == 150){ // 滑到临界点 // 判断是否过期 // 判断条件是 第一个滑块的状态是否为已点击 if(that.checkFirstLineBlockClicked(blockData[0].block)){ // 没过期 // 继续 去除旧节点 插入新节点 scrolllHeight归0 var newId = blockData[blockData.length - 1].id + 1; blockData.push(that.getNewLine(newId)); blockData.shift(); that.setData({ scrollHeight: 0, blockData: blockData.concat().reverse() }); return; } // 过期 // 报错 that.handleWrong("请点击白块!游戏结束", score); return; } currentScrollHeight = currentScrollHeight - speed; that.setData({ scrollHeight: currentScrollHeight }); }, 20); }, checkFirstLineBlockClicked: function(blockDataLine){ for(var i = 0; i < blockDataLine.length; i++){ if(blockDataLine[i] == 2){ return true; } } return false; }, getBlockBlackToGray: function(line, blockData){ for(var i = 0; i < blockData.length; i++){ if(blockData[i].id == line){ var currentArray = blockData[i].block; for(var j = 0; j < currentArray.length; j++){ if(currentArray[j] == 1){ currentArray[j] = 2; return; } } } } }, getClickableBlockLine: function(blockData){ var line = 0; for(var i = 0; i < blockData.length; i++){ var block = blockData[i].block; for(var j = 0; j < block.length; j++){ // 行内四个元素 有1即可 if(block[j] == 1){ return blockData[i].id; } } } return line; }, getNewLine: function(i){ // 生成一个标准的数据 var orderArray = [0,0,0,0]; // 生成一个随机数 var randomNum = Math.floor(Math.random() * 4); // 赋值给对应的obj orderArray[randomNum] = 1; return {id: i, block: orderArray}; }, handleWrong: function(text, score){ this.setData({ canRun: false }); wx.showToast({ title: text, icon: 'cancel', duration: 2000, complete: function(){ // 将此分数存入全局变量 app.globalData.currentScore = score; // 若此分数比最高分数还高 将其存入本地 if(score > app.globalData.speedScore){ app.globalData.speedScore = score; wx.setStorageSync('speedScore',score); } var timer = setTimeout(function(){ wx.redirectTo({ url: '../end/end?type=speed&score=' + score }) clearTimeout(timer); }, 2000); } }) } })
(2)play.json
代码如下:
{}
(3)play.wxml
代码如下:
<!--play.wxml--> <view class="score">{{score}}</view> <view class="play-box" style="bottom: {{scrollHeight}}px"> <block wx:for="{{blockData}}" wx:for-index="i" wx:key="i"> <view class="block-line" id="line-{{blockData[i].id}}"> <block wx:for="{{blockData[i].block}}" wx:key="*this" wx:for-index="j"> <view wx:if="{{blockData[i].block[j] == 0}}" id="block-{{blockData[i].id}}-{{j}}-{{0}}" class="block" bindtap="handleClickWhite"></view> <view wx:elif="{{blockData[i].block[j] == 1}}" class="block black" id="block-{{blockData[i].id}}-{{j}}-{{1}}" bindtap="handleClickBlack"></view> <view wx:else class="block gray" id="block-{{blockData[i].id}}-{{j}}-{{2}}" bindtap="handleClickGray"></view> </block> </view> </block> </view>
(4)play.wxss
代码如下:
/*page { display: flex; flex-direction: column-reverse; }*/ .score { position: fixed; top: 2rem; left: 50%; font-size: 3rem; color: #fc2736; transform: translate(-50%, 0); z-index: 10; } .play-box { width: 100%; position: fixed; left: 0; bottom: 0; } .block-line { width: 100%; overflow: hidden; } .block { width: 5rem; height: 8rem; float: left; border-left: 1px solid #000000; border-right: 1px solid #000000; box-sizing: border-box; transition: .2s all; } .black { background: #000000; } .gray { background: #ffffff; }
6. 游戏结束配置(end)
(1)end.js
代码如下:
var app = getApp() Page({ data: { currentScore: 0, gameType: "", heighestScore: 0, backUrl: "" }, onLoad: function(options){ var score = options.score; var gameType = options.type; var text = {endless: "无尽模式", time: "计时模式", speed: "极速模式"}; // 从全局变量中获取分数 this.setData({ currentScore: app.globalData.currentScore, gameType: text[gameType], heighestScore: app.globalData[gameType + "Score"], backUrl: '../'+gameType+'/play' }); } })
(2)end.json
代码如下:
{}
(3)end.wxml
代码如下:
<view class="current">{{gameType}}</view> <view class="current">本次分数:{{currentScore}}</view> <view class="highest">最高分数:{{heighestScore}}</view> <navigator url="{{backUrl}}" redirect class="go-again">再来一次</navigator> <navigator url="../index/index" redirect class="go-home">返回首页</navigator>
(4)end.wxss
代码如下:
/* pages/end/end.wxss */ .current { margin-top: 40rpx; margin-bottom: 40rpx; text-align: center; font-size: 1.5rem; } .highest { text-align: center; font-size: 1.5rem; } .go-again { text-align: center; display: block; width: 200rpx; margin: 200rpx auto 0 auto; padding: 20rpx; border-radius: 10rpx; color: #ffffff; background: #fc2736; } .go-home { text-align: center; display: block; width: 200rpx; margin: 100rpx auto 0 auto; padding: 20rpx; border-radius: 10rpx; color: #ffffff; background: blue; }
7. 日志文件配置(logs)
(1)logs.js
代码如下:
//logs.js var util = require('../../utils/util.js') Page({ data: { logs: [] }, onLoad: function () { this.setData({ logs: (wx.getStorageSync('logs') || []).map(function (log) { return util.formatTime(new Date(log)) }) }) } })
(2)logs.json
代码如下:
{ "navigationBarTitleText": "查看启动日志" }
(3)logs.wxml
代码如下:
<!--logs.wxml--> <view class="container log-list"> <block wx:for="{{logs}}" wx:for-item="log" wx:key="*this"> <text class="log-item">{{index + 1}}. {{log}}</text> </block> </view>
(4)logs.wxss
代码如下:
.log-list { display: flex; flex-direction: column; padding: 40rpx; } .log-item { margin: 10rpx; }
三、别踩白块效果实现
1. 首页效果