文章首發於個人 GitHub 博客
上一篇文章:《Chrome 小恐龍遊戲源碼探究二 -- 讓地面動起來》 實現了地面的移動。這一篇文章中,將實現效果:一、瀏覽器失焦時遊戲暫停,聚焦遊戲繼續。 二、開場動畫。 三、進入街機模式。css
街機模式的效果就是:遊戲開始後,進入全屏模式。例如:git
能夠看到,進入街機模式以前,有一段開場動畫。咱們先來實現一下這個開場動畫。github
這裏先只實現地面的開場動畫,小恐龍的後續再去實現。
首先修改 CSS 樣式:web
.offline .runner-container { position: absolute; top: 35px; - width: 100%; + width: 44px; max-width: 600px; height: 150px; overflow: hidden; }
讓 canvas
初始只顯示 44px
的寬度。canvas
而後在 Runner
的原型鏈上添加方法:segmentfault
Runner.prototype = { // 遊戲被激活時的開場動畫 // 將 canvas 的寬度調整到最大 playIntro: function () { if (!this.activated && !this.crashed) { this.playingIntro = true; // 正在執行開場動畫 // 定義 CSS 動畫關鍵幀 var keyframes = '@-webkit-keyframes intro { ' + 'from { width:' + Trex.config.WIDTH + 'px }' + 'to { width: ' + this.dimensions.WIDTH + 'px }' + '}'; // 將動畫關鍵幀插入頁面中的第一個樣式表 document.styleSheets[0].insertRule(keyframes, 0); this.containerEl.style.webkitAnimation = 'intro .4s ease-out 1 both'; this.containerEl.style.width = this.dimensions.WIDTH + 'px'; // 監聽動畫。當觸發結束事件時,設置遊戲爲開始狀態 this.containerEl.addEventListener(Runner.events.ANIMATION_END, this.startGame.bind(this)); this.setPlayStatus(true); // 設置遊戲爲進行狀態 this.activated = true; // 遊戲彩蛋被激活 } else if (this.crashed) { // 這個 restart 方法的邏輯這裏先不實現 this.restart(); } }, // 設置遊戲爲開始狀態 startGame: function () { this.playingIntro = false; // 開場動畫結束 this.containerEl.style.webkitAnimation = ''; }, };
補充數據:瀏覽器
Runner.events = { // ... + ANIMATION_END: 'webkitAnimationEnd', };
這裏用到了小恐龍類裏的數據,咱們先臨時定義一下(後面講到小恐龍那一章時,須要把這段臨時代碼刪除):函數
function Trex() {} Trex.config = { WIDTH: 44, };
而後在 Runner
的 update
方法中調用上面定義的 playIntro
方法:動畫
Runner.prototype = { update: function () { // ... if (this.playing) { this.clearCanvas(); + // 剛開始 this.playingIntro 未定義 !this.playingIntro 爲真 + if (!this.playingIntro) { + this.playIntro(); // 執行開場動畫 + } + // 直到開場動畫結束再移動地面 + if (this.playingIntro) { + this.horizon.update(0, this.currentSpeed); + } else { + deltaTime = !this.activated ? 0 : deltaTime; this.horizon.update(deltaTime, this.currentSpeed); + } } // ... }, };
解釋一下這段代碼:this
if (this.playingIntro) { this.horizon.update(0, this.currentSpeed); } else { deltaTime = !this.activated ? 0 : deltaTime; this.horizon.update(deltaTime, this.currentSpeed); }
當程序走 if
邏輯的時候,this.horizon.update
接收到的第一個參數爲 0
,這樣在這個方法內部計算出來的位移也是 0
。因此只要還在執行開場動畫,地面就不會移動。當程序走 else
邏輯的時候,開場動畫執行完畢,此時 playIntro
函數已經執行結束,this.activated
值爲 true
,deltaTime
值大於零,計算出的地面位移就再也不爲 0
。
這樣,就實現了地面的開場動畫:
查看添加或修改的代碼, 戳這裏
接下來要實現的效果是:瀏覽器窗口失焦時遊戲暫停,聚焦時遊戲繼續。
在 Runner
原型鏈上添加方法,來判斷瀏覽器窗口是否失焦:
Runner.prototype = { // 當頁面失焦時,暫停遊戲,不然進行遊戲 onVisibilityChange: function (e) { if (document.hidden || document.webkitHidden || e.type == 'blur' || document.visibilityState != 'visible') { this.stop(); } else if (!this.crashed) { this.play(); } }, play: function () { if (!this.crashed) { this.setPlayStatus(true); this.paused = false; this.time = getTimeStamp(); this.update(); } }, stop: function () { this.setPlayStatus(false); this.paused = true; cancelAnimationFrame(this.raqId); this.raqId = 0; }, };
在 startGame
方法中添加對 blur、focus
事件的監聽:
Runner.prototype = { startGame: function () { // ... + window.addEventListener(Runner.events.BLUR, + this.onVisibilityChange.bind(this)); + window.addEventListener(Runner.events.FOCUS, + this.onVisibilityChange.bind(this)); }, };
補充數據:
Runner.events = { // ... + BLUR: "blur", + FOCUS: "focus" };
效果以下:
查看添加或修改的代碼, 戳這裏
在 Runner
原型鏈上添加方法:
Runner.prototype = { // 設置進入街機模式時 canvas 容器的縮放比例 setArcadeModeContainerScale: function () { var windowHeight = window.innerHeight; var scaleHeight = windowHeight / this.dimensions.HEIGHT; var scaleWidth = window.innerWidth / this.dimensions.WIDTH; var scale = Math.max(1, Math.min(scaleHeight, scaleWidth)); var scaledCanvasHeight = this.dimensions.HEIGHT * scale; // 將 canvas 橫向佔滿屏幕,縱向距離頂部 10% 窗口高度處 var translateY = Math.ceil(Math.max(0, (windowHeight - scaledCanvasHeight - Runner.config.ARCADE_MODE_INITIAL_TOP_POSITION) * Runner.config.ARCADE_MODE_TOP_POSITION_PERCENT)) * window.devicePixelRatio; this.containerEl.style.transform = 'scale(' + scale + ') translateY(' + translateY + 'px)'; }, // 開啓街機模式(全屏) setArcadeMode: function () { document.body.classList.add(Runner.classes.ARCADE_MODE); this.setArcadeModeContainerScale(); }, };
補充數據:
Runner.config = { // ... + ARCADE_MODE_INITIAL_TOP_POSITION: 35, // 街機模式時,canvas 距頂部的初始距離 + ARCADE_MODE_TOP_POSITION_PERCENT: 0.1, // 街機模式時,canvas 距頁面頂部的距離,佔屏幕高度的百分比 }; Runner.classes = { // ... + ARCADE_MODE: 'arcade-mode', };
定義 CSS 類 arcade-mode
裏的樣式:
.arcade-mode, .arcade-mode .runner-container, .arcade-mode .runner-canvas { image-rendering: pixelated; max-width: 100%; overflow: hidden; } .arcade-mode .runner-container { left: 0; right: 0; margin: auto; transform-origin: top center; transition: transform 250ms cubic-bezier(0.4, 0.0, 1, 1) .4s; z-index: 2; }
最後調用 setArcadeMode
方法,就能夠進入街機模式:
Runner.prototype = { startGame: function () { + this.setArcadeMode(); // 進入街機模式 // ... }, };
效果以下:
查看添加或修改的代碼, 戳這裏
Demo 體驗地址:https://liuyib.github.io/blog/demo/game/google-dino/arcade-mode/
上一篇 | 下一篇 |
Chrome 小恐龍遊戲源碼探究二 -- 讓地面動起來 | Chrome 小恐龍遊戲源碼探究四 -- 隨機繪製雲朵 |