基于Vue和Vuex实现俄罗斯方块小游戏

用 Vue、Vuex 做俄罗斯方块

本项目灵感来源于 React 版的俄罗斯方块,由于对其实现原理较感兴趣,而且相比于 React 更喜欢 Vue, 于是把 React 版的重构为了 Vue 版的,大致思路是把组件当成一个个函数,保证一个输入(props)能得到一个确定的输出(view),然后对不同方法也是做同样处理,对于 Redux 使用 Vuex 精简化

效果预览

正常速度的录制,体验流畅。

响应式

不仅指屏幕的自适应,而是 在PC使用键盘、在手机使用手指的响应式操作

数据持久化

此处为语雀视频卡片,点击链接查看:persistence.mp4

Vuex 状态预览

此处为语雀视频卡片,点击链接查看:vuex.mp4

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 个版本下的支持情况

可以看到 IE 阵营与大部分安卓机不能使用,其他 ok。

3、游戏在体验上的优化

  • 技术:
    • 按下方向键水平移动和竖直移动的触发频率是不同的,游戏可以定义触发频率,代替原生的事件频率,源代码:/src/unit/event.js ;

    • 左右移动可以 delay 掉落的速度,但在撞墙移动的时候 delay 的稍小;在速度为 6 级时 通过 delay 会保证在一行内水平完整移动一次;

    • 对按钮同时注册 touchstartmousedown 事件,以供响应式游戏。当 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 版本核心代码基本相同,但在编写组件的时候遇到了几个问题,比如:

  1. 如何把 React 组件改写成 Vue 的,我的思路是把组件当成函数,保证一个输入(props)能得到一个确定的输出(view),然后对不同方法也是做同样处理, React 的 setState 会触发 render 方法,所以可以在 methods 自定义 render 方法再在 state 变化后手动触发 render 方法

  2. 生命周期,简单来说, React 的 componentWillMount 对应 Vue 的 beforeMount, React 的 componentDidMount 对应 Vue 的 mounted,React 的用来优化性能的 shouldComponentUpdate 在 Vue 里并不需要,不需要手动优化这也是我喜欢 Vue 的一点

  3. Vue 没有 React 的 componentWillReceiveProps 的生命周期,我的解决方法是使用 watch 配合 deep:true 来监听 props 的变化,如:

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

  4. 在必要时候使用 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-log

5、架构差异

Redux 的数据流向是通过 mapStateToProps 把 store 的状态转化为 props 然后通过 connect 函数注入到 根组件,根组件再把这些 props 传入不同组件,当 store 的状态变化,根组件会重新 render, 更新子组件上的 props,子组件再 根据新 props 重新 render

而 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、开发

安装

复制代码
npm install

运行

复制代码
npm run dev

浏览自动打开 http://localhost:8080

多语言

在 i18n.json 配置多语言环境,使用"lan"参数匹配语言如:https://Binaryify.github.io/vue-tetris/?lan=en

打包编译

复制代码
npm run build

dist 文件夹下生成结果。

相关推荐
加班是不可能的,除非双倍日工资3 小时前
css预编译器实现星空背景图
前端·css·vue3
wyiyiyi4 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip4 小时前
vite和webpack打包结构控制
前端·javascript
excel4 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国5 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼5 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy5 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
草梅友仁5 小时前
草梅 Auth 1.4.0 发布与 ESLint v9 更新 | 2025 年第 33 周草梅周报
vue.js·github·nuxt.js
ZXT5 小时前
promise & async await总结
前端
Jerry说前后端5 小时前
RecyclerView 性能优化:从原理到实践的深度优化方案
android·前端·性能优化