├── .babelrc ├── .gitignore ├── README.md ├── build └── doBuild.js ├── demo └── tetris.html ├── dist └── wzwTetirs.min.js ├── package.json └── src ├── wzwTetirs.js ├── wzw_levels.js ├── wzw_stufs.js └── wzw_util.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Kotlin template 3 | # Compiled class file 4 | *.class 5 | .idea/ 6 | *.iml 7 | node_modules/ 8 | 9 | # Log file 10 | *.log 11 | 12 | # BlueJ files 13 | *.ctxt 14 | 15 | # Mobile Tools for Java (J2ME) 16 | .mtj.tmp/ 17 | 18 | # Package Files # 19 | *.jar 20 | *.war 21 | *.nar 22 | *.ear 23 | *.zip 24 | *.tar.gz 25 | *.rar 26 | 27 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 28 | hs_err_pid* 29 | 30 | ### Maven template 31 | target/ 32 | pom.xml.tag 33 | pom.xml.releaseBackup 34 | pom.xml.versionsBackup 35 | pom.xml.next 36 | release.properties 37 | dependency-reduced-pom.xml 38 | buildNumber.properties 39 | .mvn/timing.properties 40 | .mvn/wrapper/maven-wrapper.jar 41 | 42 | ### Java template 43 | # Compiled class file 44 | *.class 45 | 46 | # Log file 47 | *.log 48 | 49 | # BlueJ files 50 | *.ctxt 51 | 52 | # Mobile Tools for Java (J2ME) 53 | .mtj.tmp/ 54 | 55 | # Package Files # 56 | *.jar 57 | *.war 58 | *.nar 59 | *.ear 60 | *.zip 61 | *.tar.gz 62 | *.rar 63 | 64 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 65 | hs_err_pid* 66 | 67 | ### Linux template 68 | *~ 69 | 70 | # temporary files which can be created if a process still has a handle open of a deleted file 71 | .fuse_hidden* 72 | 73 | # KDE directory preferences 74 | .directory 75 | 76 | # Linux trash folder which might appear on any partition or disk 77 | .Trash-* 78 | 79 | # .nfs files are created when an open file is removed but is still being accessed 80 | .nfs* 81 | 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wzwtetris 简介 - intro 2 | 俄罗斯方块 tetris,小时候玩的那种掌上王中王游戏机上就有俄罗斯方块,本项目还原了里面的俄罗斯方块的效果。游戏共有23关,随着游戏越往后进行关卡越高难度越大,下降速度越快。 3 | 4 | # 在线预览 - preview 5 | 6 | ![效果](http://file.microanswer.cn/222.gif) 7 | 8 | [点击立即试玩](https://www.microanswer.cn/tetris.html) 9 | 10 | # 使用 - use 11 | 12 | 通过 jsdelivr,可以快速将 github 上的资源用于cdn公共资源,所以,你可以使用下方的连接将本游戏组件引入你的网页里: 13 | 14 | ```html 15 | 16 | ``` 17 | 18 | 然后你就可以愉快的使用了: 19 | 20 | ```html 21 |
22 | 37 | ``` 38 | 39 | # 选项配置 - option 40 | 41 | 游戏在外观上提供了许多可配置项,下面列出了`option`支持的所有参数及其默认值, 整个 `option` 都是选填的。 42 | 43 | ```javascript 44 | var option = { 45 | gameWidth: 320, /* 游戏视窗宽度。 */ 46 | gameHeight: 430, /* 游戏视窗高度。 */ 47 | splitPosition: 222, /* 左边面板和右边面板的分割点,数值以从左到右计算。 */ 48 | drawColor1: '#010700', /* 画笔浓颜色 - 就是界面上很黑的那个。 */ 49 | drawColor2: '#ccebce', /* 画笔浅颜色 - 就是点阵区域后面你感觉有一层灰色的那个。 */ 50 | bgColor: '#dbffdd', /* 背景颜色。 */ 51 | lineWidth: 1, /* 画笔粗细 - canvas 绘制时的 lineWidth 参数, 同时它也是 中间分割线的粗细。 */ 52 | fontSize: 17, /* 文字大小。 */ 53 | fontSpace: 17, /* 文字行间距。 */ 54 | atomwidthCount: 10, /* 游戏区域,横向的点阵个数。 */ 55 | atomheightCount: 20, /* 游戏区域,竖向的点阵个数。 */ 56 | atomSpace: 5, /* 点阵间隙大小。 */ 57 | atomBorder: 3, /* 点阵外轮廓粗细。 */ 58 | atomInset: 1, /* 点阵中间的间隙大小。 */ 59 | useInnerKeyBoardEvent: true /* 是否使用键盘支持。 */ 60 | } 61 | ``` 62 | 63 | # 方法列表 - methods 64 | 65 | ```javascript 66 | 67 | // 开始游戏。 68 | game.startGame(); 69 | 70 | // 暂停游戏,再次调用则恢复游戏。 71 | game.pauseGame(); 72 | 73 | // 开启极速模式,开启后会在下一个材料开始的时候自动关闭。你也可以手动调用turboModeOFF方法在当前材料还没下降到底部时提前关闭。 74 | game.turboModeON(); 75 | 76 | // 关闭极速模式。 77 | game.turboModeOFF(); 78 | 79 | // 旋转变换材料,当材料所处的空间不足以完成旋转时,不会旋转。 80 | game.rotateStuff(); 81 | 82 | // 将材料左移动。 83 | game.left(); 84 | 85 | // 将材料右移动。 86 | game.right(); 87 | 88 | // 复位/重置游戏。 89 | game.resetGame(); 90 | 91 | // 获取当前是否暂停状态。 92 | game.isPaused(); 93 | 94 | // 获取当前成绩。 95 | game.getScore(); 96 | 97 | // 获取当前等级。 98 | game.getLevel(); 99 | 100 | // 获取当前正在下落的材料,可能是 null。 101 | game.getCuffStuf(); 102 | 103 | ``` 104 | 105 | 106 | # 博文 - blog 107 | 108 | [立即查看博文](https://www.microanswer.cn/blog/68) 109 | 110 | # 历史版本 - history 111 | 112 | - 1.0.3 公共文件已转为es5格式代码,之前版本的都是夹杂着部分es6语法可能运行不正常。 113 | - 1.0.2 修复暂停后还能左右移动、变型的BUG。优化代码结构。 114 | - 1.0.1 (1)修复堆砌到顶部后,再消一行,顶部的材料没有跟随下落。(2)加入一次消除4行则多加一分的加分逻辑。 115 | - 1.0.0 发布第一版本。 -------------------------------------------------------------------------------- /build/doBuild.js: -------------------------------------------------------------------------------- 1 | let webpack = require("webpack"); 2 | let path = require("path"); 3 | 4 | const webpackCfg = { 5 | entry: "./src/wzwTetirs.js", 6 | output: { 7 | path: path.resolve(__dirname, '../dist'), 8 | filename: 'wzwTetirs.min.js' 9 | }, 10 | mode: 'production', 11 | module: { 12 | rules: [ 13 | { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" } 14 | ] 15 | } 16 | }; 17 | 18 | console.log("开始..."); 19 | webpack(webpackCfg, function (er, stats) { 20 | if (er) { 21 | console.log("出错了:", er); 22 | } else { 23 | console.log("完成:", stats.toString()); 24 | } 25 | }); -------------------------------------------------------------------------------- /demo/tetris.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 俄罗斯方块 - Microanswer 8 | 91 | 92 | 93 |
94 | 95 |
96 | 97 |
98 | 99 |
100 | 101 |
102 | 103 |
104 |
105 | 106 | 107 | 108 |
109 |
110 | 111 |
112 |
113 | 114 | 115 |
116 | 117 |
118 |
119 |
120 |
121 |     122 |     123 | 124 |
125 |
126 |
127 |
128 | 开始      129 | 暂停      130 | 复位 131 |
132 |
133 | 134 |

135 | 玩法介绍:
136 | 1.点击[开始]按钮开始新游戏。
137 | 2.游戏过程可以点击[暂停]。
138 | 3.[复位]按钮可以重置游戏到初始状态。
139 | 4.如果你用电脑,可以使用方向键来控制。
140 | [W、↑]变型,[S、↓]下降,[A、←]左,[D、→]右 141 |

142 |

143 | 关于:
144 | 作者:Microanswer
145 | 首页:www.microanswer.cn
146 | 源码:查看源码
147 | 151 |

152 | 153 | 154 |
155 | 156 | 157 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /dist/wzwTetirs.min.js: -------------------------------------------------------------------------------- 1 | !function(n){var t={};function e(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return n[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}e.m=n,e.c=t,e.d=function(n,t,o){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},e.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},e.t=function(n,t){if(1&t&&(n=e(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(e.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var r in n)e.d(o,r,function(t){return n[t]}.bind(null,r));return o},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},e.p="",e(e.s=0)}([function(n,t,e){var o=e(1),r=e(2),i=r.LEVELS,a=r.SCORE_LEVELS,l=e(3);window.WzwTetirs=function(n,t){(t=t||{}).hasOwnProperty("useInnerKeyBoardEvent")||(t.useInnerKeyBoardEvent=!0);var e,r,f=t.gameWidth||320,u=t.gameHeight||430,c=t.splitPosition||222,s=t.drawColor1||"#010700",h=t.drawColor2||"#ccebce",d=t.bgColor||"#dbffdd",m=t.lineWidth||1,v=t.fontSize||17,g=t.fontSpace||17,p=t.atomwidthCount||10,w=t.atomheightCount||20,y=null,S=null,b=t.atomSpace||5,x=t.atomBorder||3,T=t.atomInset||1,C=3,k=-3,E=null,P=null,O=null,A=null,F=null,L=!1,N=!1,q=!0,M=!1,j=0,R=0,_=0,W={87:function(){Q()},38:function(){Q()},83:function(){H()},40:function(){H()},65:function(){U()},37:function(){U()},68:function(){X()},39:function(){X()},32:function(){Q()}},I={83:function(){J()},40:function(){J()}};function B(){var n=Math.floor(Math.random()*o.length);return[].concat(o[n])}function D(n){if(O&&E&&A&&!M){for(var t=C+n,e=0;ep-1&&(t=p-o-1),!(k+e<0)&&1===A[k+e][t+o]))return;O=G(t,k,E),C=t}}function V(){for(var n=[],t=0;t=4&&(R+=1);var r=a[String(R)];r>_&&(_=r)}}var i=!1;l.each(n,(function(t,e){l.scroll(0,O[e].length-1,{goo:function(n){var t=O[e].length;n=n>=t?t:n;for(var o=0;o0;){var t=n.shift();l.eachNum(t,1,(function(n){var t=n-1;A[n]=[].concat(A[t]),0===t&&(A[t]=[],l.eachNum(0,p-1,(function(n){A[t][n]=0})))}))}N=!1,O=l.arrCopy(A)}),50))}},200)}))}function G(n,t,e){for(var o=l.arrCopy(A),r=0;r=w||1!==e[r][i]||(o[t+r][n+i]=e[r][i]);return o}function K(n,t,e,o,r,i){var a=n+e*(b+y)-y,l=t+o*(b+S)-S,f=r.strokeStyle,u=r.lineWidth,c=r.fillStyle;r.strokeStyle=i,r.fillStyle=i,r.lineWidth=x,r.beginPath(),r.rect(a,l,y,S),r.stroke(),r.fillRect(a+x+T,l+x+T,y-2*x-2*T,S-2*x-2*T),r.strokeStyle=f,r.lineWidth=u,r.fillStyle=c}function z(n){n.strokeStyle=s,n.fillStyle=d,n.lineCap="butt",n.lineWidth=m,n.clearRect(0,0,f,u),n.fillStyle=s,function(n){for(var t=1;t<=p;t++)for(var e=1;e<=w;e++)K(0,0,t,e,n,h);if(O)for(t=0;t=0;t--)if(F[t])for(e=F[t].length-1;e>=0;e--)1===F[t][e]&&K(0,0,e+1,t+1,n,s)}(n),n.beginPath(),n.moveTo(c,0),n.lineTo(c,u),n.stroke(),n.closePath(),function(n){var t=v;n.font="italic normal normal "+v+"px arial",n.fillText("Score",c+b,t),n.font="normal normal bold "+v+"px arial",t+=v,n.fillText(String(R),c+b,t),n.font="italic normal normal "+v+"px arial",t=t+v+g,n.fillText("Next",c+b,t);for(var e=1;e<=4;e++)for(var o=1;o<=4;o++)if(K(c,t,e,o,n,h),P){if(1!==P[o-1][e-1])continue;K(c,t,e,o,n,s)}n.font="italic normal normal "+v+"px arial",t=t+v+g+4*(S+b),n.fillText("Level",c+b,t),n.font="normal normal bold "+v+"px arial",t+=v,n.fillText(String(_),c+b,t),n.font="italic normal normal "+v+"px arial",t=t+v+g,n.fillText("Best",c+b,t),n.font="normal normal bold "+v+"px arial",t+=v,n.fillText(String(j),c+b,t),M&&(n.font="italic normal normal "+v+"px arial",t=t+v+g,n.fillText("Paused",c+b,t))}(n)}function H(){O&&E&&(L=!0)}function J(){L=!1}function Q(){if(O&&E&&!M){for(var n=[[],[],[],[]],t=0;t=w)return;if(C+r>=p||C+r<0)return;if(1===A[k+o][C+r])return}}O=G(C,k,E=n)}}function U(){D(-1)}function X(){D(1)}function Y(){var n;q||(q=!0,R>j&&(j=R,window.localStorage&&window.localStorage.setItem("bestscore",j)),n=function(){q=!0,O=null,A=null,E=null,P=null,N=!1,L=!1,R=0,_=0,C=3,k=-3},l.scroll(w,0,{goo:function(n){n=n>w-1?w-1:n,F||(F=new Array(w)),l.eachNum(n,w-1,(function(n){F[n]=[],l.eachNum(0,p-1,(function(t){F[n][t]=1}))}))},end:function(t){l.eachNum(0,w-1,(function(n){F[n]=[],l.eachNum(0,p-1,(function(t){F[n][t]=1}))})),n&&n(),setTimeout((function(){l.scroll(0,w-1,{goo:function(n){n=n>w-1?w-1:n,l.eachNum(0,n,(function(n){F[n]=void 0}))},end:function(n){F=null}})}),100)}}))}e=function(n){var t=document.createElement("canvas");t.width=f,t.height=u,n.appendChild(t);var e=t.getContext("2d");return e.width=f,e.height=u,e.canvas.width=f,e.canvas.height=u,e.translate(.5,.5),e}(function(n){var t=document.querySelector("#"+n);return t.style.width=f+"px",t.style.height=u+"px",t.style.border="1px solid gray",t.style.backgroundColor=d,t}(n)),function(){var n=c-m;y=(n-=b*(p+1))/p;var t=u;S=(t-=b*(w+1))/w,j=window.localStorage&&window.localStorage.getItem("bestscore")||0}(),t.useInnerKeyBoardEvent&&(document.onkeydown=function(n){var t=W[String(n.keyCode)];t&&(n.stopPropagation(),t())},document.onkeyup=function(n){var t=I[String(n.keyCode)];t&&(n.stopPropagation(),t())}),r=e,function n(){z(r),window.requestAnimationFrame?window.requestAnimationFrame(n):window.webkitRequestAnimationFrame?window.webkitRequestAnimationFrame(n):setTimeout(n,33)}(),this.startGame=function(){if(q){q=!1,O=[],A=[];for(var n=0;n=(L?3:i[_])){var t=!1;E||(E=P,P=null,t=!!E),E||(E=B(),P=null,t=!!E),P||(P=B(),t=!!E),t&&J();var o=function(n,t,e,o,r){var i=!1;n:for(var a=e.length-1;a>=0;a--)for(var f=e[a].length-1;f>=0;f--)if(1===e[a][f]){var u=t+a;if(!(u<0)){var c=n+f;if(!(c<0)){if(u!==w-1&&1!==r[u+1][c]||(i=!0),0===u&&i)return-1;if(i)break n}}}return i||(t=k=t+1,o=O=G(n,t,e)),i&&(A=l.arrCopy(o)),i?1:0}(C,k,E,O,A);1===o?(E=null,C=3,k=-3,V()):-1===o&&Y(),e=Date.now()}q||(window.requestAnimationFrame?window.requestAnimationFrame(n):window.webkitRequestAnimationFrame?window.webkitRequestAnimationFrame(n):setTimeout(n,5))}()}},this.pauseGame=function(){!q&&O&&A&&(M=!M)},this.turboModeON=H,this.turboModeOFF=J,this.rotateStuff=Q,this.left=U,this.right=X,this.resetGame=Y,this.isPaused=function(){return M},this.getScore=function(){return R},this.getLevel=function(){return R},this.getCuffStuf=function(){return E}}},function(n,t){n.exports=[[[0,0,0,0],[0,0,0,0],[1,1,1,1],[0,0,0,0]],[[0,0,0,0],[0,0,0,0],[1,1,1,1],[0,0,0,0]],[[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],[[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],[[0,0,0,0],[0,1,1,0],[0,1,1,0],[0,0,0,0]],[[0,0,0,0],[0,1,1,0],[0,1,1,0],[0,0,0,0]],[[0,0,0,0],[0,1,1,0],[0,1,1,0],[0,0,0,0]],[[0,0,0,0],[0,1,1,0],[0,1,1,0],[0,0,0,0]],[[0,0,0,0],[0,1,0,0],[0,1,1,1],[0,0,0,0]],[[0,0,0,0],[0,1,1,0],[0,1,0,0],[0,1,0,0]],[[0,0,0,0],[0,1,1,1],[0,0,0,1],[0,0,0,0]],[[0,0,0,0],[0,0,1,0],[0,0,1,0],[0,1,1,0]],[[0,0,0,0],[0,1,1,1],[0,1,0,0],[0,0,0,0]],[[0,0,0,0],[0,1,1,0],[0,0,1,0],[0,0,1,0]],[[0,0,0,0],[0,0,0,1],[0,1,1,1],[0,0,0,0]],[[0,1,0,0],[0,1,0,0],[0,1,1,0],[0,0,0,0]],[[0,0,0,0],[0,1,1,1],[0,0,1,0],[0,0,0,0]],[[0,0,0,0],[0,0,1,0],[0,1,1,0],[0,0,1,0]],[[0,0,0,0],[0,0,1,0],[0,1,1,1],[0,0,0,0]],[[0,0,0,0],[0,0,1,0],[0,0,1,1],[0,0,1,0]],[[0,0,0,0],[0,1,1,0],[0,0,1,1],[0,0,0,0]],[[0,0,0,0],[0,1,1,0],[0,0,1,1],[0,0,0,0]],[[0,0,0,0],[0,0,1,0],[0,1,1,0],[0,1,0,0]],[[0,0,0,0],[0,0,1,0],[0,1,1,0],[0,1,0,0]],[[0,0,0,0],[0,0,1,1],[0,1,1,0],[0,0,0,0]],[[0,0,0,0],[0,0,1,1],[0,1,1,0],[0,0,0,0]],[[0,0,0,0],[0,1,0,0],[0,1,1,0],[0,0,1,0]],[[0,0,0,0],[0,1,0,0],[0,1,1,0],[0,0,1,0]]]},function(n,t){n.exports={LEVELS:[800,700,600,550,500,450,400,350,300,250,200,150,130,100,90,80,70,60,50,40,30,20,10,5],SCORE_LEVELS:{50:1,100:2,150:3,200:4,250:5,300:6,350:7,400:8,450:9,500:10,550:11,600:12,700:13,850:14,1e3:15,1100:16,1300:17,1500:18,2e3:19,2500:20,3e3:21,4e3:22,5e3:23}}},function(n,t){n.exports=new function(){this.scroll=function(n,t,e,o){var r=n,i=r=r||0,a=t-i;if(0!==a){var l=!1,f=o||500,u=Date.now();!function n(){setTimeout((function(){var o=Date.now()-u,r=o/f,c=Math.ceil(i+r*a);l||e&&e.goo&&e.goo(c),f<=o?(l=!0,e.end(t)):setTimeout(n,1e3/60)}),1e3/60)}()}else e&&e.end&&e.end(t)},this.printArr=function(n){var t="";this.each(n,(function(n,e){t+=n+":"+e.join(" ")+"\n"})),console.log(t)},this.arrCopy=function(n){for(var t=[],e=0;et)for(var o=n;o>=t;o--)e&&e(o);else for(o=n;o<=t;o++)e&&e(o)},this.each=function(n,t){for(var e=0;e atomheightCount - 1) ? atomheightCount - 1 : val; 170 | if (!resetAtoms) {resetAtoms = new Array(atomheightCount);} 171 | Util.eachNum(val, atomheightCount - 1, function (row) { 172 | resetAtoms[row] = []; 173 | Util.eachNum(0, atomwidthCount - 1, function (num) { 174 | resetAtoms[row][num] = 1; 175 | }); 176 | }); 177 | }, 178 | end: function (end) { 179 | Util.eachNum(0, atomheightCount - 1, function (row) { 180 | resetAtoms[row] = []; 181 | Util.eachNum(0, atomwidthCount - 1, function (num) { 182 | resetAtoms[row][num] = 1; 183 | }); 184 | }); 185 | cb && cb(); 186 | 187 | setTimeout(function () { 188 | Util.scroll(0, atomheightCount - 1, { 189 | goo: function (val) { 190 | val = (val > atomheightCount - 1) ? atomheightCount - 1 : val; 191 | 192 | Util.eachNum(0, val, function (row) { 193 | resetAtoms[row] = undefined; 194 | }); 195 | }, 196 | end: function (end) { 197 | resetAtoms = null; 198 | } 199 | }); 200 | }, 100); 201 | } 202 | }) 203 | } 204 | 205 | /* 重置游戏。 */ 206 | function _resetGame () { 207 | gameover = true; 208 | gameAtoms = null; 209 | staticStuffs = null; 210 | currStuff = null; 211 | nextStuff = null; 212 | succAniming = false; 213 | turbo = false; 214 | score = 0; 215 | level = 0; 216 | stuffOffsetX = STUFF_START_X_OFFSET; 217 | stuffOffsetY = STUFF_START_Y_OFFSET; 218 | } 219 | 220 | /* 移动当前正在掉落的材料。 count 为移动几格,负数则是左移。 */ 221 | function _moveCurrStuff (count) { 222 | if (gameAtoms && currStuff && staticStuffs && (!pause)) { 223 | var targetOffsetX = stuffOffsetX + count; 224 | 225 | for (var i = 0; i < currStuff.length; i++) { 226 | for (var j = 0; j < currStuff[i].length; j++) { 227 | if (currStuff[i][j] !== 1) { 228 | continue; 229 | } 230 | if (targetOffsetX + j < 0) { 231 | /* 传入的目标位数会导致移动到屏幕左边的外面。则强制让其在屏幕内。 */ 232 | targetOffsetX = -j; 233 | } else if (targetOffsetX + j > atomwidthCount - 1) { 234 | /* 传入的目标位数会导致移动到屏幕右边的外面。则强制让其在屏幕内。 */ 235 | targetOffsetX = atomwidthCount - j - 1; 236 | } 237 | 238 | /* 材料还没完全下降到屏幕内 */ 239 | if (stuffOffsetY + i < 0) continue; 240 | 241 | /* 判断新位置上是否有材料了,有就不能进行移动 */ 242 | if (staticStuffs[stuffOffsetY + i][targetOffsetX + j] === 1) { 243 | return; 244 | } 245 | 246 | } 247 | } 248 | 249 | gameAtoms = _addStuffToGameAtoms(targetOffsetX, stuffOffsetY, currStuff); 250 | stuffOffsetX = targetOffsetX; 251 | } 252 | } 253 | 254 | /* 检查堆砌成功的行,并进行消除。 */ 255 | function checkSuccessLine() { 256 | 257 | var successLine = []; 258 | 259 | for (var i = 0; i < staticStuffs.length; i++) { 260 | 261 | var isSuccess = true; 262 | for (var j = 0; j < staticStuffs[i].length; j++) { 263 | if (staticStuffs[i][j] !== 1){ 264 | isSuccess = false; 265 | break; 266 | } 267 | } 268 | 269 | 270 | /* 成功一行就+1分*/ 271 | if (isSuccess) { 272 | succAniming = true; 273 | score += 1; 274 | successLine.push(i); 275 | 276 | /* 如果一次消了4行,再加1分 */ 277 | if (successLine.length >= 4) { 278 | score += 1; 279 | } 280 | 281 | /*提升等级*/ 282 | var newLevel = SCORE_LEVELS[String(score)]; 283 | if (newLevel > level) { 284 | level = newLevel; 285 | } 286 | } 287 | } 288 | 289 | 290 | var ended = false; 291 | var onAnimEnd = function () { 292 | if (ended) { 293 | return; 294 | } 295 | ended = true; 296 | 297 | setTimeout(function () { 298 | /*动画完成后重整数组,将消除的行上面的依次向下整理*/ 299 | while (successLine.length > 0) { 300 | var rowm = successLine.shift(); 301 | Util.eachNum(rowm, 1, function (row) { 302 | var lastRow = row - 1; 303 | 304 | staticStuffs[row] = [].concat(staticStuffs[lastRow]); 305 | 306 | if (lastRow === 0) { 307 | staticStuffs[lastRow] = []; 308 | Util.eachNum(0, atomwidthCount - 1, function (num) { 309 | staticStuffs[lastRow][num] = 0; 310 | }); 311 | 312 | } 313 | }); 314 | } 315 | succAniming = false; 316 | gameAtoms = Util.arrCopy(staticStuffs); 317 | }, 50); 318 | }; 319 | 320 | /* 在数组里将消除的行全部置为0,通过此操作使得界面上的行有个从左到右消减的动画 */ 321 | Util.each(successLine, function (index, value) { 322 | Util.scroll(0, gameAtoms[value].length - 1, { 323 | goo: function (process) { 324 | var l = gameAtoms[value].length; 325 | /*scroll方法有bug吗,process应该在to范围内啊,但是出现了超过to的现象,所以这里判断一下*/ 326 | process = process >= l ? l : process; 327 | for (var j = 0; j < process; j++) { 328 | gameAtoms[value][j] = 0; 329 | } 330 | }, 331 | end: function (end) { 332 | for (var j = 0; j < gameAtoms[value].length; j++) { 333 | gameAtoms[value][j] = 0; 334 | } 335 | 336 | /* 将消除的行剩下的重整 */ 337 | onAnimEnd(); 338 | } 339 | }, 200); 340 | }); 341 | } 342 | 343 | /* 将 currStuff 材料通过指定的横向偏移量 stuffOffsetX和stuffOffsetY 进行融合,并将 stuffOffsetY 下降1格。*/ 344 | /* 如果 currStuff 材料已经到达了 gameAtoms 底部或已有材料的底部将返回 1, 检查游戏结束将返回 -1,其他的放回0表示正常。 */ 345 | function _GameArrMerge (stuffOffsetX, mStuffOffsetY, currStuff, mGameAtoms, mStaticStuffs) { 346 | 347 | var grounded = false; 348 | 349 | w:for (var i = currStuff.length - 1; i >= 0 ; i--) { 350 | for (var j = currStuff[i].length - 1; j >= 0; j--) { 351 | if (currStuff[i][j] !== 1) continue; 352 | /* 判断此元素下降一格后是否触底 */ 353 | 354 | 355 | var c_stuffOffsetY = mStuffOffsetY + i; 356 | if (c_stuffOffsetY < 0) continue; 357 | 358 | var c_stuffOffsetX = stuffOffsetX + j; 359 | if (c_stuffOffsetX < 0) continue; 360 | 361 | /* 素材达到最低边, 或素材下一个位置有已确认的素材,则认为到底了。 */ 362 | if (c_stuffOffsetY === atomheightCount - 1 || mStaticStuffs[c_stuffOffsetY + 1][c_stuffOffsetX] === 1) { 363 | grounded = true; 364 | } 365 | 366 | /* 素材本身出现的位置都已经是頂部了,这绝逼是玩家玩到顶了。 */ 367 | if (c_stuffOffsetY === 0 && grounded) { 368 | /* 游戏结束 */ 369 | return -1; 370 | } 371 | 372 | if (grounded) { 373 | break w; 374 | } 375 | } 376 | } 377 | 378 | /* 将 currStuff 融合到 mGameAtoms 里面 */ 379 | if (!grounded) { 380 | mStuffOffsetY = stuffOffsetY = mStuffOffsetY + 1; 381 | mGameAtoms = gameAtoms = _addStuffToGameAtoms(stuffOffsetX, mStuffOffsetY, currStuff); 382 | } 383 | 384 | /* 如果触底了,则将 mGameAtoms 保存一份为 mStaticStuffs */ 385 | if (grounded) { 386 | staticStuffs = Util.arrCopy(mGameAtoms); 387 | } 388 | 389 | 390 | return grounded ? 1 : 0; 391 | } 392 | 393 | /* 将指定stuff 添加到 游戏数组 staticStuffs 里面,按照指定位置添加,然后返回添加完成的结果,这个结果预期是赋值给gameAtoms的。 */ 394 | /* 本方法本身不会修改 staticStuffs。 */ 395 | function _addStuffToGameAtoms (stuffOffsetX, stuffOffsetY, stuff) { 396 | var mGameAtoms = Util.arrCopy(staticStuffs); 397 | 398 | for (var i = 0; i < stuff.length; i++) { 399 | for (var j = 0; j < stuff[i].length; j++) { 400 | if ( 401 | stuffOffsetY + i < 0 || 402 | stuffOffsetX +j < 0 || 403 | stuffOffsetY + i >= atomheightCount || 404 | stuff[i][j] !== 1 405 | ) { 406 | continue; 407 | } 408 | mGameAtoms[stuffOffsetY + i][stuffOffsetX + j] = stuff[i][j]; 409 | } 410 | } 411 | 412 | return mGameAtoms; 413 | } 414 | 415 | /* startX 所有点阵起始计算横坐标点。 */ 416 | /* startY 所有点阵起始计算竖坐标点。 */ 417 | /* 绘制一个点阵,通过 2 个index下标确定绘制具体哪一个点阵。 */ 418 | /* 下标的数值规则是:从左上角到右上交 widthIndex 1~10。 */ 419 | /* 从左上角到左下交 heightIndex 1~20。 */ 420 | /* c 绘制颜色。 */ 421 | function renderAtom (startX, startY, widthIndex, heightIndex, ctx, c) { 422 | var x = startX + (widthIndex * (atomSpace + atomWidth)) - atomWidth; 423 | var y = startY + (heightIndex * (atomSpace + atomHeight)) - atomHeight; 424 | 425 | var osc = ctx.strokeStyle; 426 | var osw = ctx.lineWidth; 427 | var ofc = ctx.fillStyle; 428 | ctx.strokeStyle = c; 429 | ctx.fillStyle = c; 430 | ctx.lineWidth = atomBorder; 431 | ctx.beginPath(); 432 | ctx.rect(x, y, atomWidth, atomHeight); 433 | ctx.stroke(); 434 | 435 | ctx.fillRect( 436 | x + atomBorder + atomInset, 437 | y+atomBorder+atomInset, 438 | atomWidth-(2*atomBorder)-(2*atomInset), 439 | atomHeight-(2*atomBorder)-(2*atomInset) 440 | ); 441 | 442 | ctx.strokeStyle = osc; 443 | ctx.lineWidth = osw; 444 | ctx.fillStyle = ofc; 445 | } 446 | 447 | /* 绘制左边游戏面板。 */ 448 | function renderLeft (ctx) { 449 | /* 先绘制点阵背景 */ 450 | for (var i = 1; i <= atomwidthCount; i++) { 451 | for (var j = 1; j <= atomheightCount; j++) { 452 | renderAtom(0, 0, i,j, ctx, drawColor2); 453 | } 454 | } 455 | 456 | /* 绘制游戏数组 */ 457 | if (gameAtoms) { 458 | for (i = 0; i < gameAtoms.length; i++) { 459 | for (j = 0; j < gameAtoms[i].length; j++) { 460 | if (gameAtoms[i][j] !== 1) continue; 461 | renderAtom(0, 0, j + 1, i + 1, ctx, drawColor1); 462 | }} 463 | } 464 | 465 | 466 | /* 绘制重置动画 */ 467 | if (resetAtoms) { 468 | for (i = resetAtoms.length - 1; i >= 0; i--) { 469 | if (resetAtoms[i]) { 470 | for (j = resetAtoms[i].length - 1; j >= 0 ; j--) { 471 | if (resetAtoms[i][j] !== 1) continue; 472 | renderAtom(0, 0, j + 1, i + 1, ctx, drawColor1); 473 | } 474 | } 475 | } 476 | } 477 | } 478 | 479 | /* 绘制右边 下一个方块,游戏成绩。 */ 480 | function renderRight (ctx) { 481 | 482 | /* 绘制游戏成绩 */ 483 | var yOffset = fontSize; 484 | ctx.font = "italic normal normal " + fontSize + "px arial"; 485 | ctx.fillText("Score", splitPosition + atomSpace, yOffset); 486 | ctx.font = "normal normal bold " + fontSize + "px arial"; 487 | yOffset = yOffset + fontSize; 488 | ctx.fillText(String(score), splitPosition + atomSpace, yOffset); 489 | 490 | ctx.font = "italic normal normal " + fontSize + "px arial"; 491 | yOffset = yOffset + fontSize + fontSpace; 492 | ctx.fillText("Next", splitPosition + atomSpace, yOffset); 493 | /* 绘制接下来要使用的材料。 */ 494 | for (var i = 1; i <= 4; i++) { 495 | for (var j = 1; j <= 4; j++) { 496 | /* 绘制背景 */ 497 | renderAtom(splitPosition, yOffset, i, j, ctx, drawColor2); 498 | 499 | /* 绘制预备材料 */ 500 | if (nextStuff) { 501 | if (nextStuff[j - 1][i - 1] !== 1) continue; 502 | renderAtom(splitPosition, yOffset, i, j, ctx, drawColor1); 503 | } 504 | } 505 | } 506 | 507 | /* 绘制等级 */ 508 | ctx.font = "italic normal normal " + fontSize + "px arial"; 509 | yOffset = yOffset + fontSize + fontSpace + (4 * (atomHeight + atomSpace)); 510 | ctx.fillText("Level", splitPosition + atomSpace, yOffset); 511 | ctx.font = "normal normal bold " + fontSize + "px arial"; 512 | yOffset = yOffset + fontSize; 513 | ctx.fillText(String(level), splitPosition + atomSpace, yOffset); 514 | 515 | /* 绘制最佳成绩 */ 516 | ctx.font = "italic normal normal " + fontSize + "px arial"; 517 | yOffset = yOffset + fontSize + fontSpace; 518 | ctx.fillText("Best", splitPosition + atomSpace, yOffset); 519 | ctx.font = "normal normal bold " + fontSize + "px arial"; 520 | yOffset = yOffset + fontSize; 521 | ctx.fillText(String(bestscore), splitPosition + atomSpace, yOffset); 522 | 523 | /* 绘制暂停提示 */ 524 | if (pause) { 525 | ctx.font = "italic normal normal " + fontSize + "px arial"; 526 | yOffset = yOffset + fontSize + fontSpace; 527 | ctx.fillText("Paused", splitPosition + atomSpace, yOffset); 528 | } 529 | } 530 | 531 | /* 此方法会被循环调用,用于绘制游戏内容。 */ 532 | function onRender (ctx) { 533 | ctx.strokeStyle = drawColor1; 534 | ctx.fillStyle = bgColor; 535 | ctx.lineCap = 'butt'; 536 | ctx.lineWidth = lineWidth; 537 | ctx.clearRect(0, 0, gameWidth, gameHeight); 538 | ctx.fillStyle = drawColor1; 539 | 540 | /* 绘制游戏区域 */ 541 | renderLeft(ctx); 542 | 543 | /* 绘制分割线 */ 544 | ctx.beginPath(); 545 | ctx.moveTo(splitPosition, 0); 546 | ctx.lineTo(splitPosition, gameHeight); 547 | ctx.stroke(); 548 | ctx.closePath(); 549 | 550 | /* 绘制右边状态值区域 */ 551 | renderRight(ctx); 552 | } 553 | 554 | /* 开始执行绘制。 */ 555 | function render (ctx) { 556 | (function loop () { 557 | onRender(ctx); 558 | if (window.requestAnimationFrame) { 559 | window.requestAnimationFrame(loop); 560 | } else if (window.webkitRequestAnimationFrame) { 561 | window.webkitRequestAnimationFrame(loop); 562 | } else { 563 | setTimeout(loop, 33); 564 | } 565 | })(); 566 | } 567 | 568 | /* ------------------------------------------------------------------------------------- */ 569 | /* 上列方法均是游戏内部逻辑方法,外界不可以调用。 */ 570 | 571 | 572 | /* 下列方法均是用于操控游戏的方法,外界可以调用。 */ 573 | /* ------------------------------------------------------------------------------------- */ 574 | 575 | /* 调用此方法开始游戏。 如果在游戏进行中重复调用了此方法,将忽略。 */ 576 | function startGame () { 577 | 578 | /* 一旦此数组有值,则认为在游戏中,直接不处理。 */ 579 | if (!gameover) { 580 | return; 581 | } 582 | gameover = false; 583 | 584 | /* 初始化游戏数组为一个空数组 */ 585 | gameAtoms = []; 586 | staticStuffs = []; 587 | for (var j = 0; j < atomheightCount; j++) { 588 | gameAtoms[j] = []; 589 | staticStuffs[j] = []; 590 | for (var i = 0; i < atomwidthCount; i++) { 591 | gameAtoms[j][i] = 0; 592 | staticStuffs[j][i] = 0; 593 | } 594 | } 595 | 596 | var lastTime = 0; 597 | (function loop() { 598 | 599 | if ( 600 | (!pause) && 601 | (!gameover /* 游戏未标记为结束 */) && 602 | (!succAniming/*在消减动画时不执行下落*/) && 603 | Date.now() - lastTime >= (turbo ? TURBO_TIME_SPACE : LEVELS[level]) 604 | ) { 605 | 606 | var isNewCurrStuf = false; 607 | 608 | if (!currStuff) { 609 | currStuff = nextStuff; /* 始终优先从下一个材料获取 */ 610 | nextStuff = null; 611 | isNewCurrStuf = !!currStuff; 612 | } 613 | if (!currStuff) { 614 | currStuff = randomStuff(); /* 通过下一个材料也没能获取到材料,说明这是游戏刚开始,此处可以直接随机获取一个。 */ 615 | nextStuff = null; 616 | isNewCurrStuf = !!currStuff; 617 | } 618 | if (!nextStuff) { 619 | nextStuff = randomStuff(); /* 产生下一个新材料 */ 620 | isNewCurrStuf = !!currStuff; 621 | } 622 | 623 | if (isNewCurrStuf) { 624 | turboModeOFF(); /*新材料掉下来时关闭急速模式*/ 625 | } 626 | 627 | /* 将 当前掉落 的材料通过指定的横向偏移量进行融合,并将 currStuff 下降1格。 */ 628 | var grounded = _GameArrMerge(stuffOffsetX, stuffOffsetY, currStuff, gameAtoms, staticStuffs); 629 | if (grounded === 1) { 630 | /* 掉落到了能够掉落的最底部了,可以进行下一个材料的掉落了。 */ 631 | currStuff = null; 632 | stuffOffsetX = STUFF_START_X_OFFSET; 633 | stuffOffsetY = STUFF_START_Y_OFFSET; 634 | 635 | /* 检查堆砌成功了的 */ 636 | checkSuccessLine(); 637 | } else if (grounded === -1) { 638 | 639 | /* 游戏结束 */ 640 | resetGame(); 641 | } else { 642 | /* 正常下落。什么都不用处理。 */ 643 | } 644 | 645 | lastTime = Date.now(); 646 | } 647 | if (!gameover) { 648 | if (window.requestAnimationFrame) { 649 | window.requestAnimationFrame(loop); 650 | } else if (window.webkitRequestAnimationFrame) { 651 | window.webkitRequestAnimationFrame(loop); 652 | } else { 653 | setTimeout(loop, 5); 654 | } 655 | } 656 | })(); 657 | } 658 | 659 | /* 暂停游戏,再次调用则恢复。 */ 660 | function pauseGame () { 661 | if (!gameover && gameAtoms && staticStuffs) { 662 | pause = !pause; 663 | } 664 | } 665 | 666 | /* 开启急速模式,急速模式下落完成一个材料后自动关闭,也可以手动调用 turboModeOFF 方法关闭。*/ 667 | function turboModeON() { 668 | if (gameAtoms && currStuff) { 669 | turbo = true; 670 | } 671 | } 672 | 673 | /* 关闭急速模式。 */ 674 | function turboModeOFF() { 675 | turbo = false; 676 | } 677 | 678 | /* 旋转材料。 此方法只有在游戏中有效,可以将正在下落的材料进行顺时针90度旋转。 */ 679 | function rotateStuff () { 680 | if (gameAtoms && currStuff && (!pause)) { 681 | var temp = [[], [], [], []]; 682 | 683 | // 进行旋转材料 684 | for (var i = 0; i < currStuff.length; i++) { 685 | for (var j = 0; j < currStuff[i].length; j++) { 686 | 687 | var ni = j; 688 | var nj = currStuff.length - 1 - i; 689 | temp[ni][nj] = currStuff[i][j]; 690 | 691 | if (stuffOffsetY + ni < 0) { 692 | /* 材料还没下降到屏幕内 */ 693 | continue; 694 | } 695 | 696 | if (stuffOffsetY + ni >= atomheightCount) { 697 | /*变化会超出屏幕,不准变*/ 698 | return; 699 | } 700 | 701 | if (stuffOffsetX + nj >= atomwidthCount || stuffOffsetX + nj < 0) { 702 | return; 703 | } 704 | 705 | 706 | /*判断变化后的材料是否和已确定的堆砌产生重叠,产生了则不进行此次变化*/ 707 | if (staticStuffs[stuffOffsetY + ni][stuffOffsetX + nj] === 1) { 708 | return; 709 | } 710 | } 711 | } 712 | 713 | /* 变化了之后,重新赋值数组,让界面变化。 */ 714 | currStuff = temp; 715 | 716 | gameAtoms = _addStuffToGameAtoms(stuffOffsetX, stuffOffsetY, currStuff); 717 | } 718 | } 719 | 720 | /* 将材料向左边移动一格。 */ 721 | function left () { 722 | _moveCurrStuff(-1); 723 | } 724 | 725 | /* 将材料向右边移动一格。 */ 726 | function right () { 727 | _moveCurrStuff(1); 728 | } 729 | 730 | /* 结束游戏、复位。 */ 731 | function resetGame () { 732 | if (gameover) { 733 | return; 734 | } 735 | 736 | gameover = true; 737 | 738 | /* 保存最佳成绩。*/ 739 | if (score > bestscore) { 740 | bestscore = score; 741 | window.localStorage && window.localStorage.setItem("bestscore", bestscore); 742 | } 743 | 744 | resetAnim(function () { 745 | _resetGame(); 746 | }); 747 | } 748 | 749 | /* 是否暂停。 */ 750 | function isPaused () { 751 | return pause; 752 | } 753 | 754 | /* 获取当前成绩。 */ 755 | function getScore() { 756 | return score; 757 | } 758 | 759 | /* 获取当前等级。 */ 760 | function getLevel() { 761 | return score; 762 | } 763 | 764 | /* 获取当前下降的材料。 */ 765 | function getCuffStuf() { 766 | return currStuff; 767 | } 768 | 769 | gameDom = initGameDom(domId); 770 | gameCanvas = initGameCanvas(gameDom); 771 | initLogic(); 772 | initEvent(); 773 | render(gameCanvas); 774 | 775 | this.startGame = startGame; 776 | this.pauseGame = pauseGame; 777 | this.turboModeON = turboModeON; 778 | this.turboModeOFF = turboModeOFF; 779 | this.rotateStuff = rotateStuff; 780 | this.left = left; 781 | this.right = right; 782 | this.resetGame = resetGame; 783 | this.isPaused = isPaused; 784 | this.getScore = getScore; 785 | this.getLevel = getLevel; 786 | this.getCuffStuf = getCuffStuf; 787 | }; -------------------------------------------------------------------------------- /src/wzw_levels.js: -------------------------------------------------------------------------------- 1 | /* 游戏等级配置 */ 2 | 3 | /* 定义了各个等级下降的速度, 实际上数字用于settimeout时间间隔 */ 4 | var LEVELS = [800, 700, 600, 550, 500, 450, 400, 350, 300, 250, 200, 150, 130, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 5]; 5 | 6 | /* 成绩对应等级 */ 7 | var SCORE_LEVELS = { 8 | "50": 1, 9 | "100": 2, 10 | "150": 3, 11 | "200": 4, 12 | "250": 5, 13 | "300": 6, 14 | "350": 7, 15 | "400": 8, 16 | "450": 9, 17 | "500": 10, 18 | "550": 11, 19 | "600": 12, 20 | "700": 13, 21 | "850": 14, 22 | "1000": 15, 23 | "1100": 16, 24 | "1300": 17, 25 | "1500": 18, 26 | "2000": 19, 27 | "2500": 20, 28 | "3000": 21, 29 | "4000": 22, 30 | "5000": 23 31 | }; 32 | 33 | module.exports = { 34 | LEVELS, SCORE_LEVELS 35 | }; -------------------------------------------------------------------------------- /src/wzw_stufs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 游戏中下落的素材,都是来自这个数组里面。 3 | * 这里面为了保证每个材料机会相同,有可能出现重复的 4 | * 5 | * 6 | * created:Microanswer 7 | * update: 2020年1月13日09:37:22 8 | * */ 9 | 10 | var STUFS = [[ 11 | 12 | [0, 0, 0, 0], /* 长条 */ 13 | [0, 0, 0, 0], 14 | [1, 1, 1, 1], 15 | [0, 0, 0, 0]], [ 16 | 17 | [0, 0, 0, 0], /* 长条 */ 18 | [0, 0, 0, 0], 19 | [1, 1, 1, 1], 20 | [0, 0, 0, 0]], [ 21 | 22 | [0, 1, 0, 0], /* 长条 */ 23 | [0, 1, 0, 0], 24 | [0, 1, 0, 0], 25 | [0, 1, 0, 0]], [ 26 | 27 | [0, 1, 0, 0], /* 长条 */ 28 | [0, 1, 0, 0], 29 | [0, 1, 0, 0], 30 | [0, 1, 0, 0]], [ 31 | 32 | // ------------------------------------------------ 33 | 34 | [0, 0, 0, 0], /* 四块 */ 35 | [0, 1, 1, 0], 36 | [0, 1, 1, 0], 37 | [0, 0, 0, 0]], [ 38 | 39 | [0, 0, 0, 0], /* 四块 */ 40 | [0, 1, 1, 0], 41 | [0, 1, 1, 0], 42 | [0, 0, 0, 0]], [ 43 | 44 | [0, 0, 0, 0], /* 四块 */ 45 | [0, 1, 1, 0], 46 | [0, 1, 1, 0], 47 | [0, 0, 0, 0]], [ 48 | 49 | [0, 0, 0, 0], /* 四块 */ 50 | [0, 1, 1, 0], 51 | [0, 1, 1, 0], 52 | [0, 0, 0, 0]], [ 53 | 54 | // ------------------------------------------------ 55 | 56 | [0, 0, 0, 0], /* 翻7 */ 57 | [0, 1, 0, 0], 58 | [0, 1, 1, 1], 59 | [0, 0, 0, 0]], [ 60 | 61 | [0, 0, 0, 0], /* 翻7 */ 62 | [0, 1, 1, 0], 63 | [0, 1, 0, 0], 64 | [0, 1, 0, 0]], [ 65 | 66 | [0, 0, 0, 0], /* 翻7 */ 67 | [0, 1, 1, 1], 68 | [0, 0, 0, 1], 69 | [0, 0, 0, 0]], [ 70 | 71 | [0, 0, 0, 0], /* 翻7 */ 72 | [0, 0, 1, 0], 73 | [0, 0, 1, 0], 74 | [0, 1, 1, 0]], [ 75 | 76 | // ------------------------------------------------ 77 | 78 | [0, 0, 0, 0], /* 正7 */ 79 | [0, 1, 1, 1], 80 | [0, 1, 0, 0], 81 | [0, 0, 0, 0]], [ 82 | 83 | [0, 0, 0, 0], /* 正7 */ 84 | [0, 1, 1, 0], 85 | [0, 0, 1, 0], 86 | [0, 0, 1, 0]], [ 87 | 88 | [0, 0, 0, 0], /* 正7 */ 89 | [0, 0, 0, 1], 90 | [0, 1, 1, 1], 91 | [0, 0, 0, 0]], [ 92 | 93 | [0, 1, 0, 0], /* 正7 */ 94 | [0, 1, 0, 0], 95 | [0, 1, 1, 0], 96 | [0, 0, 0, 0]], [ 97 | 98 | 99 | // ------------------------------------------------ 100 | 101 | [0, 0, 0, 0], /* 土 */ 102 | [0, 1, 1, 1], 103 | [0, 0, 1, 0], 104 | [0, 0, 0, 0]], [ 105 | 106 | [0, 0, 0, 0], /* 土 */ 107 | [0, 0, 1, 0], 108 | [0, 1, 1, 0], 109 | [0, 0, 1, 0]], [ 110 | 111 | [0, 0, 0, 0], /* 土 */ 112 | [0, 0, 1, 0], 113 | [0, 1, 1, 1], 114 | [0, 0, 0, 0]], [ 115 | 116 | [0, 0, 0, 0], /* 土 */ 117 | [0, 0, 1, 0], 118 | [0, 0, 1, 1], 119 | [0, 0, 1, 0]], [ 120 | 121 | 122 | // ------------------------------------------------ 123 | 124 | [0, 0, 0, 0], /* z */ 125 | [0, 1, 1, 0], 126 | [0, 0, 1, 1], 127 | [0, 0, 0, 0]], [ 128 | 129 | [0, 0, 0, 0], /* z */ 130 | [0, 1, 1, 0], 131 | [0, 0, 1, 1], 132 | [0, 0, 0, 0]], [ 133 | 134 | [0, 0, 0, 0], /* z */ 135 | [0, 0, 1, 0], 136 | [0, 1, 1, 0], 137 | [0, 1, 0, 0]], [ 138 | 139 | [0, 0, 0, 0], /* z */ 140 | [0, 0, 1, 0], 141 | [0, 1, 1, 0], 142 | [0, 1, 0, 0]], [ 143 | 144 | 145 | // ------------------------------------------------ 146 | 147 | [0, 0, 0, 0], /* 翻z */ 148 | [0, 0, 1, 1], 149 | [0, 1, 1, 0], 150 | [0, 0, 0, 0]], [ 151 | 152 | [0, 0, 0, 0], /* 翻z */ 153 | [0, 0, 1, 1], 154 | [0, 1, 1, 0], 155 | [0, 0, 0, 0]], [ 156 | 157 | [0, 0, 0, 0], /* 翻z */ 158 | [0, 1, 0, 0], 159 | [0, 1, 1, 0], 160 | [0, 0, 1, 0]], [ 161 | 162 | [0, 0, 0, 0], /* 翻z */ 163 | [0, 1, 0, 0], 164 | [0, 1, 1, 0], 165 | [0, 0, 1, 0]] 166 | ]; 167 | 168 | module.exports = STUFS; -------------------------------------------------------------------------------- /src/wzw_util.js: -------------------------------------------------------------------------------- 1 | function Util() { 2 | 3 | /** 4 | * 滚动。 此方法并不滚动界面,而是将一个数值变化到另一个数值。 5 | * @param from 从某个值 6 | * @param to 到某个值 7 | * @param back 不停的回调 8 | * @param dur 动画执行时长,默认 500 毫秒 9 | */ 10 | this.scroll = function (from, to, back, dur) { 11 | var y = from; 12 | y = y || 0; 13 | var startY = y; 14 | 15 | var distanceY = to - startY; 16 | 17 | if (distanceY === 0) { 18 | // 没有意义的滚动 19 | back && back.end && back.end(to); 20 | return undefined 21 | } 22 | 23 | var ended = false; 24 | var time = dur || 500; 25 | var ftp = 60; 26 | 27 | var ease = function (pos) { // 要使用的缓动公式 28 | return pos; 29 | }; 30 | 31 | var startTime = Date.now(); // 开始时间 32 | // 开始执行 33 | (function dd () { 34 | setTimeout(function () { 35 | var now = Date.now(); // 当前帧开始时间 36 | var timestamp = now - startTime; // 逝去的时间(已进行动画的时间) 37 | var detal2 = ease(timestamp / time); 38 | var result2 = Math.ceil(startY + detal2 * distanceY); 39 | 40 | if (!ended) { 41 | back && back.goo && back.goo(result2); 42 | } 43 | if (time <= timestamp) { 44 | ended = true; 45 | back.end(to); 46 | } else { 47 | setTimeout(dd, 1000 / ftp); 48 | } 49 | }, 1000 / ftp); 50 | })(); 51 | }; 52 | 53 | /* 54 | 打印数组 55 | * */ 56 | this.printArr = function (arr) { 57 | var s = ""; 58 | this.each(arr, function (i, arrr) { 59 | s += (i + ":" + arrr.join(" ")) + "\n"; 60 | }); 61 | console.log(s); 62 | }; 63 | 64 | /** 65 | * 二维数组拷贝 66 | * */ 67 | this.arrCopy = function (src) { 68 | var temp = []; 69 | for (var i = 0; i < src.length; i++) { 70 | temp[i] = []; 71 | for (var j = 0; j < src[j].length; j++) { 72 | temp[i][j] = src[i][j]; 73 | } 74 | } 75 | return temp; 76 | }; 77 | 78 | /** 79 | * 循环数字 80 | * @param from 自 81 | * @param to 到 82 | * @param fun 每次调用 83 | */ 84 | this.eachNum = function (from, to, fun) { 85 | if (from > to) { 86 | 87 | for (var i = from; i >= to; i--) { 88 | fun && fun(i); 89 | } 90 | 91 | return; 92 | } 93 | for (var i = from; i <= to; i++) { 94 | fun && fun(i); 95 | } 96 | }; 97 | 98 | /* 循环数组 */ 99 | this.each = function (arr, fun) { 100 | for (var i = 0; i < arr.length; i++) { 101 | fun && fun(i, arr[i]); 102 | } 103 | } 104 | } 105 | 106 | module.exports = new Util(); --------------------------------------------------------------------------------