• Stars
    star
    267
  • Rank 153,621 (Top 4 %)
  • Language Vue
  • License
    MIT License
  • Created over 8 years ago
  • Updated almost 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

使用VueJS和WebSocket技术实现的你画我猜小游戏

项目地址:https://github.com/jrainlau/draw-something

下载&运行

git clone [email protected]:jrainlau/draw-something.git

npm install

node ws-server.js // 开启websocket服务器

npm run dev  // 运行客户端程序

然后浏览器打开localhost:8080即可

效果预览

效果预览

整体架构

因为闲得慌,一直和朋友在玩你画我猜之类的小游戏,突然想到能不能自己也做一个呢,反正闲着也是闲着,同时正好可以学习一下websocket的用法。

首先分析整体架构部分: 图片描述

可以看到,整体架构非常简单,仅仅是一台服务器和两个客户端。

  • WebSocket服务器:提供数据同步,内容分发功能,采用nodejs写成。
  • 绘图画布:进行绘图的区域,同时能够获取关键词,其绘制的内容会同步到猜图画布中。
  • 猜图画布:同步自绘图画布,输入框能够提交关键词,检测答案是否正确。

下面来看具体的代码实现。

WebSocket服务器

服务器采用node.js进行搭建,使用了ws实现websocket功能。新建一个名为ws-socket.js的文件,代码如下:

/*** ws-socket.js ***/

'use strict'
// 实例化WebSocketServer对象,监听8090端口
const WebSocketServer = require('ws').Server
  , wss = new WebSocketServer({port: 8090})

// 定义关键词数组
let wordArr = ['Monkey', 'Dog', 'Bear', 'Flower', 'Girl']

wss.on('connection', (ws) => {
    console.log('connected.')
    
    // 随机获取一个关键词
    let keyWord = ((arr) => {
            let num = Math.floor(Math.random()*arr.length)
            return arr[num]
        })(wordArr)
        
    // 当服务器接收到客户端传来的消息时
    // 判断消息内容与关键词是否相等
    // 同时向所有客户端派发消息
    ws.on('message', (message) => {
        console.log('received: %s', message)
        if (message == keyWord) {
            console.log('correct')
            wss.clients.forEach((client) => {
                client.send('答对了!!')
            })
        } else {
            console.log('wrong')
            wss.clients.forEach((client) => {
                client.send(message)
            })
        }
    })
    
    // 服务器初始化时即向客户端提供一个关键词
    wss.clients.forEach((client) => {
        client.send('keyword:' + keyWord)
    })
})

使用方法基本按照ws的文档即可。其中ws.on('message', (message) => { .. })方法会在接收到从客户端传来消息时执行,利用这个方法,我们可以从绘图画布不断地向服务器发送绘图位点的坐标,再通过.send()方法把坐标分发出去,在猜图画布中获取坐标,实现绘图数据的同步。

客户端结构

作为客户端,我选择了vue进行开发,原因是因为vue使用简单快速。事先说明,本项目仅仅作为日常学习练手的项目而非vue的使用,所以有蛮多地方我是图方便暴力使用诸如document.getElementById()之类的写法的,以后有机会再改成符合vue审美的代码吧~

客户端结构如下:

|
|-- script
|       |-- components
|       |        |-- drawing-board.vue
|       |        |-- showing-board.vue
|       |
|       |-- App.vue
|       |
|       |-- index.js
|
|-- index.html

详细代码请直接浏览项目,这里仅对关键部分代码进行剖析。

绘图画布

位于./script/components/drawing-board.vue文件即为绘图画布组件。首先我们定义一个Draw类,里面是所有绘图相关的功能。

/*** drawing-board.vue ***/


'use strict'

class Draw {
    constructor(el) {
        this.el = el
        this.canvas = document.getElementById(this.el)
        this.cxt = this.canvas.getContext('2d')
        this.stage_info = canvas.getBoundingClientRect()
        // 记录绘图位点的坐标
        this.path = {
            beginX: 0,
            beginY: 0,
            endX: 0,
            endY: 0
        }
    }
    // 初始化
    init(ws, btn) {
        this.canvas.onmousedown = () => {
            this.drawBegin(event, ws)
        }
        this.canvas.onmouseup = () => {
            this.drawEnd()
            ws.send('stop')
        }
        this.clearCanvas(ws, btn)
    }
    
    drawBegin(e, ws) {
        window.getSelection() ? window.getSelection().removeAllRanges() : document.selection.empty()
        this.cxt.strokeStyle = "#000"
        
        // 开始新的路径(这一句很关键,你可以注释掉看看有什么不同)
        this.cxt.beginPath()
        this.cxt.moveTo(
            e.clientX - this.stage_info.left,
            e.clientY - this.stage_info.top
        )
        // 记录起点
        this.path.beginX = e.clientX - this.stage_info.left
        this.path.beginY = e.clientY - this.stage_info.top

        document.onmousemove = () => {
            this.drawing(event, ws)
        }
    }
    
    drawing(e, ws) {
        this.cxt.lineTo(
            e.clientX - this.stage_info.left,
            e.clientY - this.stage_info.top
        )
        // 记录终点
        this.path.endX = e.clientX - this.stage_info.left
        this.path.endY = e.clientY - this.stage_info.top
        // 把位图坐标发送到服务器
        ws.send(this.path.beginX + '.' + this.path.beginY + '.' + this.path.endX + '.' + this.path.endY)

        this.cxt.stroke()
    }
    
    drawEnd() {
        document.onmousemove = document.onmouseup = null
    }
    
    clearCanvas(ws, btn) {
        // 点击按钮清空画布
        btn.onclick = () => {
            this.cxt.clearRect(0, 0, 500, 500)
            ws.send('clear')
        }
    }
}

嗯,相信看代码很容易就看懂了当中逻辑,关键就是在drawing()的时候要不断地把坐标发送到服务器。

定义好Draw类以后,在ready阶段使用即可:

ready: () => {
        const ws = new WebSocket('ws://localhost:8090')
        let draw = new Draw('canvas')
        // 清空画布按钮
        let btn = document.getElementById('btn')
        // 与服务器建立连接后执行
        ws.onopen = () => {
            draw.init(ws, btn)
        }
        // 判断来自服务器的消息并操作
        ws.onmessage = (msg) => {
            msg.data.split(':')[0] == 'keyword' ?
                document.getElementById('keyword').innerHTML = msg.data.split(':')[1] :
                false
        }
    }

猜图画布

猜图画布很简单,只需要定义一个canvas画布,然后接收服务器发送来的坐标并绘制即可。看代码:

ready: () => {
            'use strict'
            const ws = new WebSocket('ws://localhost:8090');
            const canvas = document.getElementById('showing')
            const cxt = canvas.getContext('2d')
            // 是否重新设定路径起点
            // 为了避免把路径起点重复定义在同一个地方
            let moveToSwitch = 1
            ws.onmessage = (msg) => {
              let pathObj = msg.data.split('.')
              cxt.strokeStyle = "#000"
              
              if (moveToSwitch && msg.data != 'stop' && msg.data != 'clear') {
                  cxt.beginPath()
                  cxt.moveTo(pathObj[0], pathObj[1])
                  moveToSwitch = 0
              } else if (!moveToSwitch && msg.data == 'stop') {
                  cxt.beginPath()
                  cxt.moveTo(pathObj[0], pathObj[1])
                  moveToSwitch = 1
              } else if (moveToSwitch && msg.data == 'clear') {
                  cxt.clearRect(0, 0, 500, 500)
              } else if (msg.data == '答对了!!') {
                  alert('恭喜你答对了!!')
              }

              cxt.lineTo(pathObj[2], pathObj[3])
              cxt.stroke()
            }

            ws.onopen = () => {
                let submitBtn = document.getElementById('submit')
                // 发送答案到服务器
                submitBtn.onclick = () => {
                    let keyword = document.getElementById('answer').value
                    ws.send(keyword)
                }
            }
        }

到这里,游戏已经可以玩啦!不过还有很多细节是有待加强和修改的,比如可以给画笔选择颜色啊,多个用户抢答计分啊等等。

后记

大半天时间鼓捣出来的玩意儿,虽然粗糙,但是学到的东西还真不少,尤其是websocket和canvas这两个我所不熟悉的领域,果然实践才能出真知。 选择ES6真的能够极大地提升工作效率,Class语法的出现简直不能更赞,作为才学习jQuery源码没多久的我来说,ES6真的非常小清新。 欢迎持续关注我的专栏,会不断送出干货哦,尽请期待!

More Repositories

1

markcook

A smart and beautiful markdown editor.
Vue
550
star
2

canjs

CanJS is a javascript interpreter, which can run JS code in JS.
JavaScript
339
star
3

vue-occupy

A Vue directive for occupying content places before the data has been loaded.
JavaScript
217
star
4

scion

Scion is a scaffold for initiating new projects from a given template.
TypeScript
180
star
5

picee

Using Github as your image hosting service.
JavaScript
166
star
6

MY-Kit

基于 Vite 的超丝滑 Vue3 组件库开发框架
CSS
162
star
7

smartour

makes website tour guide much easier.
TypeScript
156
star
8

taxi-together-client

一起打车吧微信小程序客户端
JavaScript
114
star
9

LowPolifier

Style an image with low-poly
JavaScript
98
star
10

sphinx

A very light JS library which could encode a string to an image, or decode an image to a string.
JavaScript
73
star
11

vue-skeleton

JavaScript
66
star
12

chat-input-box

Web聊天工具的富文本输入框
61
star
13

vue-donut

A template work with vue-cli, it helps you build your own VueJS basic UI component library in an easy way.
JavaScript
57
star
14

motto

A tool to show your motto in an amazing way
JavaScript
49
star
15

MintloG

使用Vuejs+Webpack+VueRouter+VueResource搭建的博客SPA
JavaScript
46
star
16

rhyke

Use morse code rhythm to awake something.
JavaScript
42
star
17

filemap

A tool for creating files structure tree map.
JavaScript
41
star
18

blog-articles

My personal blog.
HTML
39
star
19

tiny-reactive

JavaScript
31
star
20

mongoose_crud

使用express与mongoose对mongodb数据库进行增删改查操作demo
JavaScript
29
star
21

elf

一个干净,轻巧的响应式CSS框架。
CSS
26
star
22

taxi-together-server

taxi wechat mini program server
Python
23
star
23

vonut

A personal blog system made by VueJS.
JavaScript
23
star
24

online-code-runner

Online code runner, works like Codepen
Vue
21
star
25

VueComponentsCommunicateDemo

实现了vuejs组件之间的通讯问题
JavaScript
19
star
26

mog

A very easy data binding instance.
JavaScript
18
star
27

gulp-html-import

A gulp plugin which can import .html files into .html files
JavaScript
17
star
28

webpack-learning

JavaScript
16
star
29

wallpaper-downloader

Python
10
star
30

dolu

A light weight tool for photo uploading
JavaScript
9
star
31

markssue

🍘 Markdown previewer for editing Github issues.
JavaScript
8
star
32

vue-composition-api-demo

Vue
5
star
33

python-learning

记录我的python学习路线
Python
5
star
34

node-redis-missions-queue

TypeScript
5
star
35

jsdoc2configs

Convert JSDoc to any configs
TypeScript
4
star
36

TodoMVC-PWA

CSS
3
star
37

gulp-tools

gulp功能包
JavaScript
3
star
38

node-learning

nodejs学习
JavaScript
3
star
39

kaleido

A free wallpaper downloader relates to
JavaScript
2
star
40

bsdiff-cli

bsdiff globally
JavaScript
2
star
41

extract-yaml-from-markdown-plugin

extract yaml information from a markdown file automatically
JavaScript
2
star
42

rust_learning_check_list

My Rust learning check list
Rust
1
star
43

oChat-client

Vue
1
star
44

longit.js

A tool to format long number string which is longer than Number.MAX_SAFE_INTEGER and Number.MAX_VALUE.
JavaScript
1
star
45

flutter-learning

Dart
1
star
46

oChat-server

Python
1
star
47

happy_wedding

Vue
1
star
48

injector

A Chrome extension for injecting custom stylesheet to any website.
JavaScript
1
star
49

huya-tools

JavaScript
1
star
50

learning-vue-by-steps

JavaScript
1
star
51

lottie-converter

Export any frames of a lottie, convert lottie to APNG、GIF、MP4.
Vue
1
star
52

koa-learning

JavaScript
1
star
53

ts-learning

TypeScript
1
star