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.js
To build, usews
库Realize the websocket function. Create a new one namedws-socket.js
The 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 followsws
库The 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 chosevue
The reason for the development is thatvue
The 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 tovue
Aesthetic 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.vue
The file is the drawing canvas component. First we define aDraw
Class, 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 definedDraw
Class, after theready
Use 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.Class
The appearance of grammar can hardly be more admirable, learning as a talent.jQuery
The 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!