typescript 简版跳一跳
学习typescript,第一步应该是学习官方文档,理解最基础的语法。第二步开始用typescript实现一些js+css 或者canvas类型的游行。现在开始我们用ts写跳一跳
核心点:1.场景的随机创建
2.旗子的跳动
3.落脚点的判断,重点要提及的是射线判断法,参见博客
4.场景平移
5.游戏重置
6.销毁场景外方块
Ts代码:

1 1 //1.创建底座: 2 2 //2.创建跳棋: 3 3 //3.点击移动 4 4 //4.开始按压动画, 5 5 //5.放开动画 6 6 //6.跳动 7 7 //7.是否跳对 8 8 //8.移动场景 9 9 //9.创建新底座 10 10 module Jump { 11 11 interface Pos { 12 12 x: number; 13 13 y: number; 14 14 } 15 15 enum Flag { 16 16 on, in, out 17 17 } 18 18 enum Direction { 19 19 left, 20 20 right 21 21 } 22 22 let direction: Direction = Direction.right; 23 23 let diamondsList: diamonds[] = []; 24 24 let mask: JQuery<HTMLElement> = $(".game-p"); 25 25 let chess: Chess; 26 26 let score:number=0; 27 27 class MathHelp { 28 28 /** 29 29 * 返回范围内随机数[min,max] 30 30 * @param min 最小值 31 31 * @param max 最大值 32 32 */ 33 33 static RandRange(min: number, max: number): number { 34 34 return Math.floor(Math.random() * (max - min + 1) + min); 35 35 } 36 36 /** 37 37 * 根据角度求对边长度。Math.sin(randian) 38 38 * @param r 斜边长 39 39 * @param angle 角度 40 40 */ 41 41 static righttriangle(r: number, angle: number): number { 42 42 return Math.sin(Math.PI / 180 * angle) * r; 43 43 } 44 44 /** 45 45 * 射线法判断点是否在多边形内部 46 46 * @param p 待判断的点 47 47 * @param poly 多边形顶点 48 48 */ 49 49 static rayCasting(p: Pos, poly: Pos[]): Flag { 50 50 var px = p.x, 51 51 py = p.y, 52 52 flag = false 53 53 54 54 for (var i = 0, l = poly.length, j = l - 1; i < l; j = i, i++) { 55 55 var sx = poly[i].x, 56 56 sy = poly[i].y, 57 57 tx = poly[j].x, 58 58 ty = poly[j].y 59 59 60 60 // 点与多边形顶点重合 61 61 if ((sx === px && sy === py) || (tx === px && ty === py)) { 62 62 return Flag.on; 63 63 } 64 64 65 65 // 判断线段两端点是否在射线两侧 66 66 if ((sy < py && ty >= py) || (sy >= py && ty < py)) { 67 67 // 线段上与射线 Y 坐标相同的点的 X 坐标 68 68 var x = sx + (py - sy) * (tx - sx) / (ty - sy) 69 69 70 70 // 点在多边形的边上 71 71 if (x === px) { 72 72 return Flag.on; 73 73 } 74 74 75 75 // 射线穿过多边形的边界 76 76 if (x > px) { 77 77 flag = !flag 78 78 } 79 79 } 80 80 } 81 81 82 82 // 射线穿过多边形边界的次数为奇数时点在多边形内 83 83 return flag ? Flag.in : Flag.out; 84 84 } 85 85 86 86 } 87 87 class diamonds { 88 88 89 89 private width: number = 180; 90 90 private height: number = 180; 91 91 id: string = "p" + new Date().getTime();; 92 92 node: JQuery<HTMLElement>; 93 93 left: number = 0; 94 94 top: number = 0; 95 95 constructor(isauto: boolean = true, isRe = true, left: number = 0, top: number = 0) { 96 96 this.left = left; 97 97 this.top = top; 98 98 if (isauto) { 99 99 100100 let dl = MathHelp.RandRange(200, 300); 101101 let x = MathHelp.righttriangle(dl, 57); 102102 let y = MathHelp.righttriangle(dl, 33); 103103 let lastbox = diamondsList[diamondsList.length - 1]; 104104 if (isRe) { 105105 if (direction == Direction.left) { 106106 direction = Direction.right; 107107 } else if (direction == Direction.right) { 108108 direction = Direction.left; 109109 } 110110 } 111111 if (direction == Direction.right) { 112112 this.left = lastbox.left + x; 113113 } else { 114114 this.left = lastbox.left - x; 115115 } 116116 this.top = lastbox.top - y; 117117 } 118118 this.node = $(`<div id='${this.id}' class='gb' style="top:${this.top}px;left:${this.left}px"> 119119 <div class='box' style="background:url(img/c${MathHelp.RandRange(1, 4)}.png)"></div> 120120 <div class='shadw'></div> 121121 </div>`); 122122 mask.append(this.node); 123123 124124 } 125125 GetPointList(): Pos[] { 126126 var result = [{ 127127 x: this.left, 128128 y: this.top + 57 129129 }, { 130130 x: this.left + (this.width / 2), 131131 y: this.top + 4 132132 }, { 133133 x: this.left + this.width, 134134 y: this.top + 57 135135 }, { 136136 x: this.left + (this.width / 2), 137137 y: this.top + (57 * 2 - 4) 138138 }]; 139139 return result; 140140 } 141141 move(p: Pos) { 142142 this.node.css({ 143143 left: p.x + "px", 144144 top: p.y + "px", 145145 transition: "", 146146 transform: "" 147147 }); 148148 } 149149 } 150150 class Chess { 151151 private width: number = 180; 152152 private height: number = 182; 153153 left: number = 49; 154154 top: number = 531; 155155 node: JQuery<HTMLElement>; 156156 constructor() { 157157 158158 this.node =$(`<div class="chess gb"></div>`); 159159 mask.append(this.node); 160160 } 161161 jump(pt: Pos, call: Function) { 162162 let numb = 0; 163163 let t1 = setInterval(() => { 164164 165165 if (numb == 0) { 166166 this.node.animate({ 167167 left: pt.x + "px", 168168 top: pt.y + "px" 169169 }, 504); 170170 171171 } 172172 this.node.css({ 173173 "background-position": "-" + this.width * numb + "px" + " 0px" 174174 }); 175175 numb++; 176176 if (numb >= 11) { 177177 window.clearInterval(t1); 178178 call(); 179179 } 180180 181181 }, 42) 182182 183183 } 184184 GetCenter(): Pos { 185185 return { 186186 x: this.left + this.width / 2, 187187 y: this.top + this.height 188188 } 189189 } 190190 move(p: Pos) { 191191 this.node.css({ 192192 left: p.x + "px", 193193 top: p.y + "px", 194194 transition: "", 195195 transform: "" 196196 }); 197197 } 198198 } 199199 200200 class Game { 201201 static distince = 0; 202202 static time: number; 203203 /** 204204 * 初始化游戏场景 205205 */ 206206 static scence() { 207207 let d1 = new diamonds(false, false, 50, 650); 208208 diamondsList.push(d1); 209209 let d = new diamonds(true, false); 210210 diamondsList.push(d); 211211 Game.loadlisten(); 212212 chess = new Chess(); 213213 $(".againBtn").on("click",()=>{ 214214 //重置 215215 Game.GameRest(); 216216 217217 }); 218218 } 219219 /** 220220 * 重置游戏 221221 */ 222222 static GameRest(){ 223223 $(".gameEnd").css({ 224224 "z-index":-1 225225 }); 226226 diamondsList=[]; 227227 score=0; 228228 $(".jfb").html(score.toString()); 229229 $(".gb").remove(); 230230 direction=Direction.right; 231231 Game.scence(); 232232 233233 } 234234 static CreateSP() { 235235 let d = new diamonds(); 236236 diamondsList.push(d); 237237 Game.loadlisten(); 238238 } 239239 private static loadlisten() { 240240 document.addEventListener('touchstart', Game.touch, false); 241241 document.addEventListener('touchmove', Game.touch, false); 242242 document.addEventListener('touchend', Game.touch, false); 243243 } 244244 private static removelisten() { 245245 document.removeEventListener('touchstart', Game.touch, false); 246246 document.removeEventListener('touchmove', Game.touch, false); 247247 document.removeEventListener('touchend', Game.touch, false); 248248 } 249249 private static touch(e: Event) { 250250 e.preventDefault(); 251251 let currentDiamonds = diamondsList[diamondsList.length - 2]; 252252 if (e.type == "touchstart") { 253253 //挤压形变动画 254254 let scaley = 1; 255255 let chessy = 0; 256256 Game.distince = 0; 257257 Game.time = setInterval(() => { 258258 if (scaley > 0.7) { 259259 scaley -= 0.01; 260260 } 261261 if (chessy < 30) { 262262 chessy++; 263263 chess.node.css({ 264264 "transform": " scaleX(" + (1 + chessy * 0.006) + ") scaleY(" + (1 - (chessy * 0.007)) + ")" 265265 }) 266266 } 267267 Game.distince++; 268268 currentDiamonds.node.css({ 269269 "transform-origin": "bottom center", 270270 "transform": "scaleY(" + scaley + ")" 271271 }); 272272 273273 }, 50); 274274 275275 } else if (e.type == "touchend") { 276276 //1.底座还原动画 277277 //2.旗子动画轨迹 278278 //3.落脚点判断 279279 //4.场景平移,创建新底座,移除画外底座 280280 currentDiamonds.node.css({ 281281 "transform": "scaleY(1)" 282282 }); 283283 clearInterval(Game.time); 284284 Game.removelisten(); 285285 Game.Jump(Game.distince); 286286 287287 } 288288 } 289289 private static Jump(distince: number) { 290290 let dl = distince * 17; 291291 let x = MathHelp.righttriangle(dl, 57); 292292 let y = MathHelp.righttriangle(dl, 33); 293293 if (direction == Direction.left) { 294294 x = -x; 295295 } 296296 chess.left = chess.left + x; 297297 chess.top = chess.top - y; 298298 chess.jump({ x: chess.left, y: chess.top }, () => { 299299 let p = chess.GetCenter(); 300300 let last = diamondsList[diamondsList.length - 1]; 301301 let poly = last.GetPointList(); 302302 //判断是否跳在基座上 303303 let result = MathHelp.rayCasting(p, poly); 304304 if (result == Flag.in) { 305305 direction = Math.random() > 0.5 ? 1 : 0; 306306 Game.moveblock(); 307307 score++; 308308 $(".jfb").html(score.toString()); 309309 } else { 310310 //game over 311311 $(".gameEnd").css({ 312312 "z-index":999 313313 }); 314314 $(".score").html(score.toString()); 315315 console.log("game over!"); 316316 } 317317 }) 318318 319319 } 320320 /** 321321 * 移动场景 322322 */ 323323 private static moveblock() { 324324 let last = diamondsList[diamondsList.length - 1]; 325325 let p1: Pos; 326326 let x: number = 0; 327327 let y: number = 0; 328328 //以左右标准基座为左边平移 329329 if (direction == Direction.left) { 330330 p1 = { x: 50, y: 650 }; 331331 } else { 332332 p1 = { x: 400, y: 650 }; 333333 } 334334 x = p1.x - last.left; 335335 y = p1.y - last.top; 336336 337337 $(".gb").css({ 338338 "transform": "translate(" + x + "px," + y + "px)", 339339 "transition": "transform 0.9s" 340340 }); 341341 342342 343343 setTimeout(() => { 344344 //更新class中left,top属性 345345 $.each(diamondsList, (a, b) => { 346346 b.left = b.left + x; 347347 b.top = b.top + y; 348348 b.move({ x: b.left, y: b.top }); 349349 }); 350350 chess.left = chess.left + x; 351351 chess.top = chess.top + y; 352352 chess.move({ 353353 x: chess.left, 354354 y: chess.top 355355 }); 356356 Game.Destroy(); 357357 //创建新底座 358358 Game.CreateSP(); 359359 }, 1100) 360360 } 361361 //销毁画外的底座 362362 private static Destroy(){ 363363 diamondsList.forEach((item,i,list)=>{ 364364 365365 if(item.top>1008){ 366366 diamondsList.splice(i,1); 367367 $("#"+item.id).remove(); 368368 } 369369 }) 370370 } 371371 } 372372 Game.scence(); 373373 374374 }
View Code
html:

1 1 <!DOCTYPE html> 2 2 <html> 3 3 4 4 <head> 5 5 <meta charset="utf-8" /> 6 6 <title>Typescript跳一跳</title> 7 7 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" /> 8 8 <script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script> 9 9 <link rel="stylesheet" type="text/css" href="css/ts.css" /> 1010 <script type="text/javascript"> 1111 var isios = false; 1212 ! function(userAgent) { 1313 var screen_w = parseInt(window.screen.width), 1414 scale = screen_w / 640; 1515 if(/Android (\d+\.\d+)/.test(userAgent)) { 1616 var version = parseFloat(RegExp.$1); 1717 document.write(version > 2.3 ? '<meta name="viewport" content="width=640, initial-scale = ' + scale + ',user-scalable=1, minimum-scale = ' + scale + ', maximum-scale = ' + scale + ', target-densitydpi=device-dpi">' : '<meta name="viewport" content="width=640, target-densitydpi=device-dpi">'); 1818 } else { 1919 isios = true; 2020 document.write('<meta name="viewport" content="width=640, initial-scale = ' + scale + ' ,minimum-scale = ' + scale + ', maximum-scale = ' + scale + ', user-scalable=no, target-densitydpi=device-dpi">'); 2121 } 2222 }(navigator.userAgent); 2323 </script> 2424 </head> 2525 2626 <body> 2727 2828 <!--游戏页面--> 2929 <div class="game"> 3030 3131 <div class="game-p"> 3232 <!--<div class="chess gb"></div> 3333 <div id="one" class="gb"> 3434 <div class="box"></div> 3535 <div class="shadw"></div> 3636 </div>--> 3737 </div> 3838 <img class="logo" src="img/logo.png" /> 3939 <div class="jfb">0</div> 4040 <div class="toolp">距离开启宝箱还有5步</div> 4141 </div> 4242 4343 <div class="gameEnd"> 4444 4545 <div class="getScore"> 4646 4747 <p class="score">10</p> 4848 <button class="againBtn">再来一局</button> 4949 5050 </div> 5151 5252 </div> 5353 5454 <script src="js/myjump.js" type="text/javascript" charset="utf-8"></script> 5555 </body> 5656 5757 </html>
View Code
css:

1 1 html, 2 2 body { 3 3 margin: 0; 4 4 padding: 0; 5 5 height: 100%; 6 6 min-height: 1008px; 7 7 } 8 8 9 9 .game { 10 10 height: 100%; 11 11 width: 100%; 12 12 position: relative; 13 13 left: 0; 14 14 top: 0; 15 15 } 16 16 17 17 18 18 .logo { 19 19 position: absolute; 20 20 left: 30px; 21 21 top: 26px; 22 22 } 23 23 24 24 .jfb { 25 25 position: absolute; 26 26 top: 30px; 27 27 right: 60px; 28 28 font-size: 100px; 29 29 font-weight: 900; 30 30 color: #4f4e3f; 31 31 text-align: center; 32 32 line-height: 100px; 33 33 } 34 34 35 35 .toolp { 36 36 position: absolute; 37 37 bottom: 5%; 38 38 left: 30%; 39 39 width: 40%; 40 40 height: 50px; 41 41 border-radius: 50px; 42 42 color: #FFFFFF; 43 43 text-align: center; 44 44 font-size: 20px; 45 45 line-height: 50px; 46 46 background-color: #4a764e; 47 47 } 48 48 .game-p { 49 49 height: 100%; 50 50 width: 100%; 51 51 position: absolute; 52 52 left: 0; 53 53 top: 0; 54 54 background-color: #8aad8e; 55 55 } 56 56 .gb{ 57 57 position: absolute; 58 58 left: 50px; 59 59 top: 650px; 60 60 61 61 } 62 62 .box{ 63 63 height: 180px; 64 64 width: 180px; 65 65 background-image:url(../img/c1.png); 66 66 background-size: 100% 100%; 67 67 z-index: 2; 68 68 position: absolute; 69 69 } 70 70 .shadw{ 71 71 position: absolute; 72 72 left: 120px; 73 73 top: 0; 74 74 height: 133px; 75 75 width: 234px; 76 76 background-image: url(../img/s2.png); 77 77 background-size: 100% 100%; 78 78 opacity: 0.3; 79 79 transform: translateY(35px) translateX(-180px); 80 80 transform-origin: bottom center; 81 81 } 82 82 .chess{ 83 83 width: 182px; 84 84 height: 227px; 85 85 background-image: url(../img/e3.png); 86 86 background-position:0 0; 87 87 position: absolute; 88 88 top: 531px; 89 89 left: 49px; 90 90 z-index: 3; 91 91 92 92 } 93 93 .gameEnd{ 94 94 position: absolute; 95 95 top: 0; 96 96 left: 0; 97 97 width: 100%; 98 98 height: 100%; 99 99 overflow: hidden; 100100 background-color: rgba(0,0,0,.8); 101101 z-index: -1; 102102 } 103103 .getScore{ 104104 width: 492px; 105105 height: 760px; 106106 background: url(../img/getScore.png) no-repeat; 107107 background-size: 100% auto; 108108 position: absolute; 109109 top: 0; 110110 right: 0; 111111 left: 0; 112112 bottom: 0; 113113 margin: auto; 114114 } 115115 .score{ 116116 color: #dcc226; 117117 font-size: 130px; 118118 text-align: center; 119119 margin-top: 120px; 120120 font-weight: 900; 121121 122122 } 123123 .againBtn{ 124124 width: 309px; 125125 height: 87px; 126126 background: url(../img/bg.png) no-repeat; 127127 background-size: 100% 100%; 128128 font-size: 40px; 129129 color: #dcc226; 130130 text-align: center; 131131 border: none; 132132 font-weight: 900; 133133 position: absolute; 134134 left: 50%; 135135 margin-left: -154.5px; 136136 }
View Code
源码:留下邮箱,看到就发。