純 CSS 實現打地鼠游戲
只借助 CSS 而不使用 JavaScript 來實現一個網頁游戲并不需要什么新潮的技術,簡單的幾個選擇器組合就能創造出意想不到的效果,再配合上 CSS3 的“繪圖”能力,甚至可以做出不遜于 JavaScript 實現的網頁游戲。
為了參加公司內的 CSS3 比賽,我連夜做了一個 CSS3 打地鼠游戲(其實準備時間挺長的,但是報了個名就忘記這回事了,交稿前兩天被人提醒才想起來),如下:
進入游戲后,首先會有個開始按鈕,點擊開始后,游戲開始倒計時,同時不斷地有地鼠從洞里鉆出來,用鼠標點擊地鼠即可得分(總分顯示在頂部的記分板上)。15秒后游戲結束,同時顯示結束頁面。
項目代碼放在 GitHub 上,也可以訪問在線 Demo試玩。
這篇日志將介紹實現這個游戲的過程中我認為比較有意思的幾個地方。
使用 CSS 繪制地鼠
由于是個純 CSS 游戲,為了不使用圖片,所有的游戲角色都使用了 CSS 進行繪制。在這個游戲中主要有兩個元素:地洞和地鼠。地洞(你別騙我啦,那個明明就是馬里奧的小綠桶嘛)畫起來很簡單,用一個元素和其:before偽元素結合,設置好位置和尺寸,加些漸變色就可以了。稍微復雜點的是地鼠的繪制,為了實現方便,我希望能只使用一個元素來繪制(稍候會介紹原因)。繪制步驟如下:
1. 首先找一個參照物
地鼠是什么樣子的呢?雖然玩了很多次打地鼠游戲,其實我對地鼠的樣子一點印象都沒有。不過這怎么能難倒前 Go 語言愛好者的我呢!我淡定地打開了 Go 的官網,端詳了一下那個可愛的吉祥物:
我們先來把地鼠分成 3 個部分,主體、左耳朵和右耳朵。其中主體可以使用元素本身表現,設置寬高和border-radius來實現圓角:
等等,根本不像啊!
下面關鍵的地方來了,怎么實現畫眼睛和鼻子。因為 CSS 支持一個元素多個背景圖,而每個背景圖不僅可以是由url指定的函數,還可以是一個gradient類型來表示漸變。由于眼睛和鼻子都是近似圓形的,所以可以用射線漸變radial-gradient來實現。技巧在于對透明色的使用,使得漸變看起來并不是充滿整個元素的。請結合下面的代碼來理解:
2. 使用:before和:after偽元素實現耳朵
耳朵和主體一樣,也可以使用radial-gradient來繪制,這里不在贅述。效果如下:
等等!還是不對啊,下半身還沒畫呢,腳呢!
別擔心,在游戲中地鼠是不會露出下半身的。
“開始”按鈕的實現
進入游戲后,會出現開始界面,只有用戶點擊了“開始”按鈕后游戲才會開始。如果可以使用 JavaScript,那么只需要綁定按鈕的click事件來實現相應邏輯,那么使用 CSS 要怎么實現呢?
其實方法很簡單,只需要想一下 CSS/HTML 中怎么才能夠保存狀態即可。沒錯,就是input[type="checkbox"]。只要用戶點擊了按鈕,就把 checkbox 選中,接下來就可以用 CSS 的+和~選擇符來設置游戲中其他元素的屬性了。比如如果想要 checkbox 選中后令開始界面(.start)隱藏,只需要:
input[type="checkbox"]:checked + .start { display: none; }
借此我們可以把“點擊按鈕開始游戲”簡化為“點擊按鈕選中 checkbox”,很容易想到可以使用label來實現按鈕。在 HTML 中,點擊label標簽會自動選中其對應的表單元素,所以我們只需要把 checkbox 隱藏,并把label設置成按鈕的樣子即可。如下代碼所示:
打地鼠的實現
實現打地鼠和實現“開始”按鈕的思路是一樣的,也是用戶點擊地鼠后設置其的checked屬性,不同的是這里我們使用另一種方法來繪制地鼠:地鼠本身不是label,而是input[type="radio"]。至于為什么用 radio 而不是 checkbox 稍候會解釋,在這一節中 radio 和 checkbox 都是可以的。
要注意的是需要使用appearance: none來禁用 radio 元素的默認控件樣式。
技巧:記分板
記分板的實現并不復雜,但是思路比較繞。在 CSS 中沒有辦法存放數據,使得記分板功能看起來很難實現。實際上我們可以這樣想:要實現點擊地鼠記分板加一,就要讓地鼠和記分板聯動起來。一般來說最容易想到 的聯動方式就是我們之前用過的 label + checkbox 和+/~選擇器,實際上還有另一種聯動方式,就是 radio。要知道擁有同一 name 的 一組 radio 同時只會有一個元素被選中,所以接下來就很容易想到可以讓地鼠是一個 radio,同時記分板是一個 radio,兩個元素擁有相同的 name 即可實現點擊地鼠后記分板發生變化。
下面的問題就是怎么把記分板的 radio 的選中狀態轉換成具體數字了。思路是這樣的:
- 每個 radio 都占據一定高度
- 每個分數都以一個元素的形式來展現,高度和 radio 相同
- 使用一個 viewport 來顯示當前分數,當 radio 被取消選中后,將其高度設置為 0,這樣下面的分數就會向上升,使得當前對應的分數在 viewport 中顯示出來。
整個結構如下圖所示:
示例代碼如下(點擊地鼠分數會變化):
總結
至此游戲的難點就介紹得差不多了,剩下的倒計時、地鼠鉆入鉆出都只不過是對 CSS Animation 的運用。使用純 CSS 實現游戲的過程非常有意思,也很利于提高自己對 CSS 的理解,推薦大家都嘗試一下。
來自:http://zihua.li/2015/01/implement-pure-css-game/