You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
func (hub *Hub) broadcast(message interface{}, ignore *Client) {
data, _ := json.Marshal(message)
for _, c := range hub.clients {
if c != ignore {
c.outbound <- data
}
}
}
Messages
Messages 将使用JSON作为交互格式。 每条消息将携带一个“kind”字段,以区分消息
新建messages.go文件并创建 message 包
声明所有消息“kinds”的枚举
package message
const (
// KindConnected is sent when user connects
KindConnected = iota + 1
// KindUserJoined is sent when someone else joins
KindUserJoined
// KindUserLeft is sent when someone leaves
KindUserLeft
// KindStroke message specifies a drawn stroke by a user
KindStroke
// KindClear message is sent when a user clears the screen
KindClear
)
声明一些简单的数据结构
type Point struct {
X int `json:"x"`
Y int `json:"y"`
}
type User struct {
ID string `json:"id"`
Color string `json:"color"`
}
声明所有的消息类型结构体并编写 构造函数。kind 字段在构造函数中设置
type Connected struct {
Kind int `json:"kind"`
Color string `json:"color"`
Users []User `json:"users"`
}
func NewConnected(color string, users []User) *Connected {
return &Connected{
Kind: KindConnected,
Color: color,
Users: users,
}
}
type UserJoined struct {
Kind int `json:"kind"`
User User `json:"user"`
}
func NewUserJoined(userID string, color string) *UserJoined {
return &UserJoined{
Kind: KindUserJoined,
User: User{ID: userID, Color: color},
}
}
type UserLeft struct {
Kind int `json:"kind"`
UserID string `json:"userId"`
}
func NewUserLeft(userID string) *UserLeft {
return &UserLeft{
Kind: KindUserLeft,
UserID: userID,
}
}
type Stroke struct {
Kind int `json:"kind"`
UserID string `json:"userId"`
Points []Point `json:"points"`
Finish bool `json:"finish"`
}
type Clear struct {
Kind int `json:"kind"`
UserID string `json:"userId"`
}
func (hub *Hub) onConnect(client *Client) {
log.Println("client connected: ", client.socket.RemoteAddr())
// Make list of all users
users := []message.User{}
for _, c := range hub.clients {
users = append(users, message.User{ID: c.id, Color: c.color})
}
// Notify user joined
hub.send(message.NewConnected(client.color, users), client)
hub.broadcast(message.NewUserJoined(client.id, client.color), client)
}
onDisconnect函数从 clients 中删除断开连接的客户端,并通知其他人有人离开。
func (hub *Hub) onDisconnect(client *Client) {
log.Println("client disconnected: ", client.socket.RemoteAddr())
client.close()
// Find index of client
i := -1
for j, c := range hub.clients {
if c.id == client.id {
i = j
break
}
}
// Delete client from list
copy(hub.clients[i:], hub.clients[i+1:])
hub.clients[len(hub.clients)-1] = nil
hub.clients = hub.clients[:len(hub.clients)-1]
// Notify user left
hub.broadcast(message.NewUserLeft(client.id), nil)
}
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
var isDrawing = false;
var strokeColor = '';
var strokes = [];
编写canvas处理事件
canvas.onmousedown = function (event) {
isDrawing = true;
addPoint(event.pageX - this.offsetLeft, event.pageY - this.offsetTop, true);
};
canvas.onmousemove = function (event) {
if (isDrawing) {
addPoint(event.pageX - this.offsetLeft, event.pageY - this.offsetTop);
}
};
canvas.onmouseup = function () {
isDrawing = false;
};
canvas.onmouseleave = function () {
isDrawing = false;
};
编写addPoint方法,strokes是一个画笔数组,存储所有的点。
function addPoint(x, y, newStroke) {
var p = { x: x, y: y };
if (newStroke) {
strokes.push([p]);
} else {
strokes[strokes.length - 1].push(p);
}
update();
}
socket.onmessage = function (event) {
var messages = event.data.split('\n');
for (var i = 0; i < messages.length; i++) {
var message = JSON.parse(messages[i]);
onMessage(message);
}
};
function onMessage(message) {
switch (message.kind) {
case MESSAGE_CONNECTED:
break;
case MESSAGE_USER_JOINED:
break;
case MESSAGE_USER_LEFT:
break;
case MESSAGE_STROKE:
break;
case MESSAGE_CLEAR:
break;
}
}
strokeColor = message.color;
for (var i = 0; i < message.users.length; i++) {
var user = message.users[i];
otherColors[user.id] = user.color;
otherStrokes[user.id] = [];
}
首先,我们需要创建一个用于与用户交互消息的桥梁(Hub)。这个思路类似于Gorilla's 的 chat 例子。
Client struct
创建一个 client.go 文件
为client编写一个构造方法,这里使用了UUID和随机颜色库
新建 utilities.go 文件来编程 generateColor 方法
写一个用于到Hub读取消息 read 的方法,如果有错误发生,将会通知unregistered通道
write方法从outbound通道获取消息并发送给用户。这样,服务器将能够发送消息到客户端。
在 client struct中添加 启动 和 结束 进程的方法,并且在启动方法中使用 goroutine 运行 read 和 write 方法
Hub struct
新建 hub.go 文件并声明 Hub struct
添加构造方法
添加 run 方法
编写一个将http升级到WebSockets请求的方法。 如果升级成功,客户端将被添加到 clients 中。
编写一个发送消息到客户端的方法
编写一个广播(broadcast)消息到所有客户端的方法(排除自己)
Messages
Messages 将使用JSON作为交互格式。 每条消息将携带一个“kind”字段,以区分消息
新建messages.go文件并创建 message 包
声明所有消息“kinds”的枚举
声明一些简单的数据结构
声明所有的消息类型结构体并编写 构造函数。kind 字段在构造函数中设置
Handling message flow
返回hub.go文件,添加所有缺少的功能。
onConnect函数表示客户端连接,在 run方法中调用。 它将用户的画笔颜色和其他用户的信息发送给客户端。 它还将当前连接的用户信息通知给其他在线用户。
onDisconnect函数从 clients 中删除断开连接的客户端,并通知其他人有人离开。
每当从客户端收到消息时,都会调用onMessage函数。 首先通过使用tidwall/gjson包来读取它是什么样的消息,然后分别处理每个情况。
在这个例子中,情况都是相似的。 每个消息获得用户的ID,然后转发给其他客户端。
最后,编写main.go文件
Front-end app
前端应用程序将用纯JavaScript编写。 在client目录创建index.html文件
上面的代码会创建一个画布和一个清除按钮。 以下所有JavaScript代码都编写在window.onload事件处理程序中。
Drawing on canvas
声明一些变量
编写canvas处理事件
编写addPoint方法,strokes是一个画笔数组,存储所有的点。
update 方法重绘
drawStrokes 绘制多个路径
清除 点击事件
Server communication
要与服务器通信,请首先声明一些额外的变量。
otherColors对象将保存其他客户端的颜色,其中key将是用户ID。 otherStrokes将保存绘图数据。
在addPoint函数中增加发送消息。 对于这个例子,points数组只有一个点。 理想情况下,分数将根据一些标准分批发送。
处理发送 "clear" 消息
onmessage 处理函数
对于 MESSAGE_CONNECTED 情况,设置用户的画笔颜色并用给定的信息填充“other”对象。
对于MESSAGE_USER_JOINED的情况,设置用户的颜色并准备一个空的笔划数组。
在MESSAGE_USER_LEFT的情况下,如果有人离开,需要删除他的数据,并从画布上清除他的绘画。
在MESSAGE_STROKE的情况下,更新用户的笔画数组。
对于MESSAGE_CLEAR情况,只需清除用户的笔划数组。
更新update方法以显示他人的图纸。
源码
https://github.com/chapin666/simple-drawing-backend
效果图
The text was updated successfully, but these errors were encountered: