Vue+WebSocket+ES6+Canvas Make “You Draw My Guess” Games

  canvas, es6, vue.js, websocket

Project address:https://github.com/jrainlau/draw-something

Download & Run

git clone git@github.com:jrainlau/draw-something.git
 cd draw-something
 
 Js//opens the websocket server.
 
 Npm run dev // runs the client program
 
 Then the browser opens localhost:8080

Effect preview:

效果预览

Overall structure

I have been playing games with my friends, such as you draw and guess, because I am so carefree. I suddenly thought that I could make one myself. I am also idle anyway, and at the same time I can learn the usage of websocket.

First, analyze the overall architecture:

图片描述

As you can see, the overall architecture is very simple, just one server and two clients.

  • WebSocket Server: Provides data synchronization and content distribution functions, written in nodejs.

  • Drawing canvas: the area where drawing is carried out and keywords can be obtained at the same time. The drawn contents will be synchronized with the drawing cloth.

  • Guess Picture Cloth: Synchronizing self-drawing canvas, the input box can submit keywords to check whether the answer is correct.

Let’s look at the specific code implementation.

WebSocket server

Server adoptionnode.jsTo build, usewsRealize the websocket function. Create a new one namedws-socket.jsThe code is as follows:

/*** ws-socket.js ***/
 
 'use strict'
 //Instantiate WebSocketServer object and listen on 8090 port
 const WebSocketServer = require('ws').Server
 , wss = new WebSocketServer({port: 8090})
 
 //Define keyword array
 let wordArr = ['Monkey', 'Dog', 'Bear', 'Flower', 'Girl']
 
 wss.on('connection', (ws) => {
 console.log('connected.')
 
 //randomly get a keyword
 let keyWord = ((arr) => {
 let num = Math.floor(Math.random()*arr.length)
 return arr[num]
 })(wordArr)
 
 //When the server receives the message from the client
 //Judge whether the message content is equal to the keyword
 //Send messages to all clients at the same time
 ws.on('message', (message) => {
 console.log('received: %s', message)
 if (message == keyWord) {
 console.log('correct')
 wss.clients.forEach((client) => {
 Send ('right!  !'  )
 })
 } else {
 console.log('wrong')
 wss.clients.forEach((client) => {
 client.send(message)
 })
 }
 })
 
 //A keyword is provided to the client when the server is initialized.
 wss.clients.forEach((client) => {
 client.send('keyword:' + keyWord)
 })
 })

The usage method is basically as followswsThe document of the. among themws.on('message', (message) => { .. })The method will be executed when receiving the message from the client. With this method, we can continuously send the coordinates of the drawing site to the server from the drawing canvas and then pass through the.send()Methods The coordinates were distributed, and the coordinates were obtained from the drawing cloth to realize the synchronization of drawing data.

Client structure

As a client, I chosevueThe reason for the development is thatvueThe use is simple and fast. It is stated in advance that this project is only used for daily learning and practicing, not for vue, so there are quite a few places I am trying to use violence, such asdocument.getElementById()Such as writing, later have the opportunity to change to conform tovueAesthetic code ~

The client structure is as follows:

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

Please browse the detailed codes directly.ProjectsOnly the key codes are analyzed here.

Drawing canvas

locate./script/components/Thedrawing-board.vueThe file is the drawing canvas component. First we define aDrawClass, which contains all drawing-related functions.

/*** 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()
 //Record the coordinates of the drawing site
 this.path = {
 beginX: 0,
 beginY: 0,
 endX: 0,
 endY: 0
 }
 }
 //initialization
 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"
 
 //Start a new path (this sentence is very important, you can comment it out to see what's different)
 this.cxt.beginPath()
 this.cxt.moveTo(
 e.clientX - this.stage_info.left,
 e.clientY - this.stage_info.top
 )
 //Record starting point
 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
 )
 //Record End Point
 this.path.endX = e.clientX - this.stage_info.left
 this.path.endY = e.clientY - this.stage_info.top
 //Send bitmap coordinates to the server
 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) {
 //Click the button to empty the canvas
 btn.onclick = () => {
 this.cxt.clearRect(0, 0, 500, 500)
 ws.send('clear')
 }
 }
 }

Well, I believe it is very easy to understand the logic in the code, the key is indrawing()We should send the coordinates to the server continuously.

Well definedDrawClass, after thereadyUse in stages:

ready: () => {
 const ws = new WebSocket('ws://localhost:8090')
 let draw = new Draw('canvas')
 //Clear Canvas Button
 let btn = document.getElementById('btn')
 //Execute after establishing connection with server
 ws.onopen = () => {
 draw.init(ws, btn)
 }
 //Judge and operate the message from the server
 ws.onmessage = (msg) => {
 msg.data.split(':')[0] == 'keyword' ?
 document.getElementById('keyword').innerHTML = msg.data.split(':')[1] :
 false
 }
 }

Guess the picture cloth

Guess the picture cloth is very simple, just define a canvas canvas, then receive the coordinates sent by the server and draw it. Look at the code:

ready: () => {
 'use strict'
 const ws = new WebSocket('ws://localhost:8090');
 const canvas = document.getElementById('showing')
 const cxt = canvas.getContext('2d')
 //Do you want to reset the starting point of the path
 //In order to avoid repeatedly defining the starting point of the path in the same place
 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 ==' right!  !'  ) {
 Congratulations on your answer!  !'  )
 }
 
 cxt.lineTo(pathObj[2], pathObj[3])
 cxt.stroke()
 }
 
 ws.onopen = () => {
 let submitBtn = document.getElementById('submit')
 //Send the answer to the server
 submitBtn.onclick = () => {
 let keyword = document.getElementById('answer').value
 ws.send(keyword)
 }
 }
 }

Here, the game can be played already! However, there are still many details that need to be strengthened and modified, such as the choice of colors for paintbrushes and scores for multiple users.

Postscript

Although the things churned out for most of the time are rough, they still learn a lot, especially in the two unfamiliar fields of websocket and canvas. It is true that real knowledge comes from practice.

Choosing ES6 can really greatly improve the work efficiency.ClassThe appearance of grammar can hardly be more admirable, learning as a talent.jQueryThe source code didn’t take long for me, ES6 is really very small and fresh.

Welcome to continue to pay attention to my column, will continue to send dry goods oh, please look forward to!