一個niubility的Vue遊戲,真厲害!(收藏)

2022-01-02 程式設計師黑叔

 點擊上方「程式設計師黑叔」,選擇「置頂或者星標」

用Vue、Vuex 做俄羅斯方塊

本項目靈感來源於 React 版的俄羅斯方塊,由於對其實現原理較感興趣,而且相比於 React 更喜歡 Vue, 於是把 React 版的重構為了 Vue 版的,大致思路是把組件當成一個個函數,保證一個輸入(props)能得到一個確定的輸出(view),然後對不同方法也是做同樣處理,對於 Redux 使用 Vuex 精簡化

效果預覽效果預覽

正常速度的錄製,體驗流暢。

響應式響應式

不僅指屏幕的自適應,而是在PC使用鍵盤、在手機使用手指的響應式操作:

手機數據持久化

視頻

玩單機遊戲最怕什麼?斷電。通過訂閱 store.subscribe,將state儲存在localStorage,精確記錄所有狀態。網頁關了刷新了、程序崩潰了、手機沒電了,重新打開連接,都可以繼續。

Vuex 狀態預覽(Vue DevTools extension)preview

視頻

Vuex 設計管理了所有應存的狀態,這是上面持久化的保證。

遊戲框架使用的是 Vue + Vuex

1、Web Audio Api

遊戲裡有很多不同的音效,而實際上只引用了一個音效文件:/build/music.mp3。藉助Web Audio Api能夠以毫秒級精確、高頻率的播放音效,這是<audio>標籤所做不到的。在遊戲進行中按住方向鍵移動方塊,便可以聽到高頻率的音效。

網頁音效進階

WAA 是一套全新的相對獨立的接口系統,對音頻文件擁有更高的處理權限以及更專業的內置音頻效果,是W3C的推薦接口,能專業處理「音速、音量、環境、音色可視化、高頻、音向」等需求,下圖介紹了WAA的使用流程。

流程

其中Source代表一個音頻源,Destination代表最終的輸出,多個Source合成出了Destination。原始碼:/src/unit/music.js 實現了ajax加載mp3,並轉為WAA,控制播放的過程。

WAA 在各個瀏覽器的最新2個版本下的支持情況(CanIUse)

瀏覽器兼容

可以看到IE陣營與大部分安卓機不能使用,其他ok。

Web Audio Api 學習資料:

Getting Started with Web Audio API3、遊戲在體驗上的優化按下方向鍵水平移動和豎直移動的觸發頻率是不同的,遊戲可以定義觸發頻率,代替原生的事件頻率,原始碼:/src/unit/event.js ;左右移動可以 delay 掉落的速度,但在撞牆移動的時候 delay 的稍小;在速度為6級時 通過delay 會保證在一行內水平完整移動一次;對按鈕同時註冊touchstart和mousedown事件,以供響應式遊戲。當touchstart發生時,不會觸發mousedown,而當mousedown發生時,由於滑鼠移開事件元素可以不觸發mouseup,將同時監聽mouseout 模擬 mouseup。原始碼:/src/components/keyboard/index.js;監聽了 visibilitychange 事件,當頁面被隱藏\切換的時候,遊戲將不會進行,切換回來將繼續,這個focus狀態也被寫進了 Vuex 中。所以當用手機玩來電話時,遊戲進度將保存;PC開著遊戲幹別的也不會聽到gameover,這有點像 ios 應用的切換。在任意時刻刷新網頁,(比如消除方塊時、遊戲結束時)也能還原當前狀態;遊戲中唯一用到的圖片是,其他都是CSS;遊戲兼容 Chrome、Firefox、IE9+、Edge等;可以在遊戲未開始時制定初始的棋盤(十個級別)和速度(六個級別);一次消除1行得100分、2行得300分、3行得700分、4行得1500分;方塊掉落速度會隨著消除的行數增加(每20行增加一個級別);4、開發中的經驗梳理,以及如何把 React 項目重構為 Vue 版本

Vue 版本和 React 版本核心代碼基本相同,但在編寫組件的時候遇到了幾個問題,比如:

如何把 React 組件改寫成 Vue 的,我的思路是把組件當成函數,保證一個輸入(props)能得到一個確定的輸出(view),然後對不同方法也是做同樣處理, React 的 setState 會觸發 render 方法,所以可以在 methods 自定義 render 方法再在 state 變化後手動觸發 render 方法

生命周期,簡單來說, React 的 componentWillMount 對應 Vue 的 beforeMount, React 的 componentDidMount 對應 Vue 的 mounted,React 的用來優化性能的 shouldComponentUpdate 在 Vue 裡並不需要,不需要手動優化這也是我喜歡 Vue 的一點

Vue 沒有 React 的componentWillReceiveProps 的生命周期,我的解決方法是使用 watch 配合 deep:true 來監聽 props 的變化,如:

  watch: {    $props: {      deep: true,      handler(nextProps) {        //xxx      }    }  }

在必要時候使用 jsx 和 'render' 函數,是的, vue 支持 jsx,在這個項目中matrix 組件 的功能邏輯較複雜,使用 template 模版來渲染組件已經不合適了, React 每次 setState 會觸發 'render' 方法,所以我們可以在 methods自定義 'render' 方法再在 state 變化後手動觸發 'render' 方法,但是這個方法對有複雜邏輯的組件來說會變得很繁瑣,我的解決方法是通過 Vue 的 jsx 轉換插件babel-plugin-transform-vue-jsx來使用 jsx 語法對頁面進行渲染,當 props 或 state 變化了自動觸發 'render' 方法,另外要注意的是 vue 的 jsx 和 React 的 jsx 書寫上有一點的差異, 當 'render' 方法存在時,template 語法會失效. 'render' 函數一個比較實用的用處是在開發類似 React-log 之類的不需要渲染 html 只需要執行一些方法的組件時 template 會顯得很多餘,因為這時候並不需要渲染 dom 了,如果用了 'render' 函數,簡單的在 'render' 函數裡 return false 就行,如: react-log5、架構差異

Redux 的數據流向是通過 mapStateToProps 把 store 的狀態轉化為 props 然後通過connect 函數注入到 根組件,根組件再把這些 props 傳入不同組件,當 store 的狀態變化,根組件會重新 render, 更新子組件上的 props,子組件再 根據新 props重新 render引用知乎一個答友的回答https://www.zhihu.com/question/47686258來說就是:

「單例store的數據在react中可以通過view組件的屬性(props)不斷由父模塊「單向」傳遞給子模塊,形成一個樹狀分流結構。如果我們把redux比作整個應用的「心肺」 (redux的flux功能像心臟,reducer功能像肺部毛細血管),那麼這個過程可以比作心臟(store)將氧分子(數據)通過動脈毛細血管(props)送到各個器官組織(view組件)末端的view組件,又可以通過flux機制,將攜帶交互意圖信息的action反饋給store。這個過程有點像將攜帶代謝產物的「紅細胞」(action)通過靜脈毛細血管又泵回心臟(store)action流回到store以後,action以參數的形式又被分流到各個具體的reducer組件中,這些reducer同樣構成一個樹狀的hierarchy。這個過程像靜脈血中的紅細胞(action)被運輸到肺部毛細血管(reducer組件)接收到action後,各個child reducer以返回值的形式,將最新的state返回給parent reducer,最終確保整個單例store的所有數據是最新的。這個過程可以比作肺部毛細血管的血液充氧後,又被重新泵回了心臟」

而 vuex 的思路則不同,任何組件都隨時可以通過 this.$store.state.xxx 獲取 store 上的數據,更自由,從 store 實例中讀取狀態最簡單的方法就是在計算屬性中返回某個狀態:

computed: {    keyboard () {      return this.store.state.keyboard    }  }

調用 store.commit 提交 payload修改數據 或者 store.dispatch 提交 mutation 間接修改 store 上的數據, commit 和 dispatch 的區別在於 commit 用於同步修改狀態, dispatch 用於異步修改狀態,異步完成需要再調用 commit,一般簡單的需求只需要 commit 一個 payload 就行,只要 store 上的數據變了,組件都會自動重新渲染

6、開發瀏覽自動打開 http://localhost:8080多語言 在 i18n.json 配置多語言環境,使用"lan"參數匹配語言如:https://Binaryify.github.io/vue-tetris/?lan=en和http://binaryify.github.io/vue-tetris/?lan=zh7、幸福時刻

8、一起嗨起來

戳左下"閱讀原文"即可玩一玩!

相關焦點

  • 華為G7 Plus教你如何詮釋niubility
    繼前段時間的"然並卵"、"城會玩"等詞語流行之火,這幾天網絡上出現了很多英文的流行詞語,其中一個名為"niubility"的英文詞語是最火爆的。那麼在手機圈中,有沒有稱得上是"niubility"的手機呢?有的網友會說,那肯定是iPhone和三星啊,小編也非常肯定網友的回答,在高端機中,蘋果和三星確實非常厲害,可在2000元檔的中端市場,蘋果沒有機型,三星則功能不全,反倒是國內手機廠商更注重這個市場。
  • 基於Vue實現一個有點意思的拼拼樂小遊戲
    技術棧如下:vue-cli4 基於vue的腳手架Xuery 筆者基於原生js二次封裝的dom庫vue mvvm庫因為該應用屬於H5遊戲,為了清亮化筆者沒有採用第三方ui庫, 如果大家想採用基於正文我們先來看看遊戲的預覽界面:在線體驗地址:傳送門本文的算法實現方式在之前的拼拼樂文章中已經說明,這裡主要介紹核心算法, 至於vue-cli的使用方法,筆者之前也寫過對應的文章,大家可以研究學習一下。
  • vue常用框架和組件代碼,抓緊收藏轉發吧
    /zh-cn4.vant  有贊團隊開發的移動端ui組件庫https://youzan.github.io/vant/5. vue-ztree 樹形控制項還是很好用的https://github.com/lisiyizu/vue-ztree    6. vueAdmin  基於vue和element快速搭建項目結構頁面
  • 前端技術:開發一個vue中央事件總線插件vue-bus
    大家都知道,一個中央事件總線bus,可以作為一個簡單的組件傳遞數據,用於解決跨級和兄弟組件通信問題,那麼,這篇文字,我將使用這種思想,將bus封裝為一個Vue的插件,可以在所有的組件間任意使用,而不需要導入bus。
  • Vue3 的 7 種和 Vue2 的 12 種組件通信,值得收藏
    /child.vue"import { ref, reactive } from "vue"export default {    data(){        return {            msg1:"這是傳級子組件的信息1"        }    },    setup(){        // 創建一個響應式數據
  • Vue Loader 篇(下):編寫一個單文件 Vue 組件
    框架開始之前,需要添加 Bootstrap 到 Vue CLI 項目,由於目前所有前端資源都已經通過 NPM 進行管理,所以需要安裝對應的依賴包:npm install bootstrap jquery popper.js然後在 src/main.js 中引入 Bootstrap 的腳本和樣式文件:import Vue from 'vue
  • 用Vue實現一個街機遊戲搖杆
    原倉庫地址:https://code.google.com/archive/p/ccjoystick/downloads在Vue裡實現這個東西沒啥用處,畢竟Vue也不是一個遊戲框架,但是誰叫Vue這個話題的熱度最高呢😁,寫文章還是希望被更多人看到嘛...
  • 【Vue.js入門到實戰教程】11-Vue Loader(下)| 編寫一個單文件 Vue 組件
    CLI 項目,由於目前所有前端資源都已經通過 NPM 進行管理,所以需要安裝對應的依賴包:npm install bootstrap jquery popper.js然後在 src/main.js 中引入 Bootstrap 的腳本和樣式文件:import Vue from 'vue'import
  • 一個後端狗的 Vue 筆記【入門級】
    這一塊某乎上有一個比較好的答案,很容易度娘到,貼了部分vue即主張較少,也就是說可以在原有系統上面,引入vue直接就可以當jquery用,使用 vue,你可以在原有大系統的上面,把一兩個組件改用它實現,當 jQuery 用;也可以整個用它全家桶開發(二) MVVM 架構 正式學習 Vue 前我們首先還需要了解一個基於前端的架構模式,也就是 MVVM ,它是
  • Vue項目實戰(八)渲染一個列表
    渲染一個列表,公眾號已經準備了vue實戰教程,如果您有需要,可以在公眾號回復「vue」獲取。製作 header.vue 和 footer.vue 組件文件。在第三篇博文中,我們規劃了我們的項目文件結構,當時保留了一個 components 的空文件夾。這裡,就是準備放我們的自定義組件的。首先,我們去創建兩個空文本文件,分別是 header.vue 文件和 footer.vue 文件。
  • Vue新玩具VueUse
    前言上次在看前端早早聊大會中, 尤大大再一次提到了 VueUse 的一個庫。好奇了一下,點看看了看。好傢夥啊, 我直接好傢夥。這不就是曾經我也想自己寫一個 vue 版的 hooks 庫嗎?(因為我覺得 vue3 和 hooks 太像了) 可是我還不太會, 你現在直接把我的夢想給破滅了,下面我們一起來看看吧!
  • 一個超詳細vue無限滾動vue-infinite-scroll插件的配置及使用詳解
    開發中總會遇到這種下拉加載的設計方案,Vue實現下拉加載最佳方案自然是使用vue-infinite-scroll來實現。接下來我們一起看下它的配置及使用方式。一般情況下會在頁尾做一個幾十像素高的「正在加載中...」,這樣的話,可以把這個div的高度設為infinite-scroll-distance的值即可。infinite-scroll-immediate-check 默認值為true,該指令表示,應該在綁定後立即檢查busy的值和是否滾動到底。
  • 實現一個簡單的Vue.js
    原文轉自 https://const_white.gitee.io/gitee-blog/blog/vue/mini-vue/Vue響應式原理圖片引自 孟思行 - 圖解 Vue 響應式原理乞丐版 mini-vue
  • Vue進階篇: vue-loader
    二、vue-loader是什麼?vue-loader 是一個 Webpack 的 loader,可以將.vue文件轉化成普通的js模塊來執行。文件是一個自定義的文件類型,用類 HTML 語法描述一個 Vue 組件。
  • Vue 新玩具 VueUse
    通俗的來說,這就是一個工具函數包,它可以幫助你快速實現一些常見的功能,免得你自己去寫,解決重複的工作內容。以及進行了基於 Composition API 的封裝。讓你在 vue3 中更加得心應手。useMouse 是一個監聽當前滑鼠坐標的一個方法,他會實時的獲取滑鼠的當前的位置。usePreferredDark 是一個判斷用戶是否喜歡深色的方法,他會實時的判斷用戶是否喜歡深色的主題。useLocalStorage 是一個用來持久化數據的方法,他會把數據持久化到本地存儲中。
  • Vue-Router源碼學習之index.js(vue-router類)
    index.js是vue-router這個類的主構造函數,所以內容上算是比較關鍵的:從圖片中我們可以看出來,這是一個ES6聲明類的方法,vue-router源碼中類的聲明都是使用類ES的語法,constructor (options: RouterOptions
  • 【Vuejs】1160- Vue 的新玩具 VueUse
    前言上次在看前端早早聊大會中, 尤大大再一次提到了 VueUse 的一個庫。好奇了一下,點看看了看。好傢夥啊, 我直接好傢夥。這不就是曾經我也想自己寫一個 vue 版的 hooks 庫嗎?(因為我覺得 vue3 和 hooks 太像了) 可是我還不太會, 你現在直接把我的夢想給破滅了,下面我們一起來看看吧!
  • Vue入門10 vue+elementUI
    -- 引入組件庫 --><script src="https://unpkg.com/element-ui/lib/index.js"></script>通過CDN的方式我們可以很容易地使用Element寫出一個Hello world頁面。<!
  • Vue全家桶&vue-router原理
    原理 Vue全家桶:vue + vue-router(路由)+ vuex(狀態管理)+ axios(請求)本次主要分析並實現簡版vue-router,從源碼中學習借鑑,更好的理解源碼vue-routerVue Router 是 Vue.js 官⽅的路由管理器。
  • 解決VUE頁面刷新,數據丟失「建議收藏」
    在vue項目中有時候會遇到一個問題,就是進行頁面刷新的時候,導致頁面的數據丟失,出現這個問題的原因是因為當用vuex做全局狀態管理的時候,store中的數據是保存在運行內存中的,頁面刷新時會重新加載vue實例,store中的數據就會被重新賦值,因此數據就丟失了,解決方式如下:方法一: