多人协作共享画板——多人画板的收尾工作
通过前面的几篇博客,基本上已经可以实现数据在网络上的传递和正确的还原了。但是现在还留有一些细节上的问题亟待解决,所以在最后一篇博客,我们来进行一些收尾的工作!目前待解决的问题?多人可以同时进行绘制,但是线条的样式变得离散了,不再连续,很难看。多人同时进行绘制时,线条的颜色和粗细会进行干扰。我们先来解决第一个问题吧,因为它比较简单。线条离散问题改进方式:在绘制前,设置context的lineCap和
通过前面的几篇博客,基本上已经可以实现数据在网络上的传递和正确的还原了。但是现在还留有一些细节上的问题亟待解决,所以在最后一篇博客,我们来进行一些收尾的工作!
目前待解决的问题?
- 多人可以同时进行绘制,但是线条的样式变得离散了,不再连续,很难看。
- 多人同时进行绘制时,线条的颜色和粗细会进行干扰。
我们先来解决第一个问题吧,因为它比较简单。
线条离散问题
改进方式:
在绘制前,设置context的lineCap
和lineJoin
样式为“round“
即可,即:
context.lineJoin = "round" // 设置连接点的样式
context.lineCap = "round" // 设置线段的末端以圆形结束
注意:
我发现似乎只设置lineCap即可。
说明:
这种方式可以起作用的原因是,线段的某段以圆形结束。这样很多的线段的某段都会有一个圆形突出,它们正好掩盖了那些离散的地方了。
改进前:
改进后:
线条颜色和大小干扰问题
我们来简单分析一下这个影响的过程!
当前用户按下鼠标,开始绘制时,会修改当前笔的颜色和大小。当用户绘制时,对面接收到数据,并且修改了颜色和线宽。然后对面就接收到了当前的用户的颜色和线宽了。
颜色的问题是:当用户按下鼠标开始画的时候,颜色是通过取色板获取的,此时是正常的,但是在画的过程中,如果接受到了其他用户的绘制数据,画笔的颜色就会被改变,然后此时继续绘制(继续绘制时取的是当前画笔的颜色)就会出现问题。这里的问题的原因是每次绘制过程中,颜色被改变了。
大小的问题是:当用户开始绘制时 ,他会选择画笔的大小。但是当用户画完一笔之后,接收到了其他用户的数据,修改了当前的线宽,当用户再次绘制时,他没有设置线宽(通常都会认为还是以上次的大小进行绘制),实际上线宽已经改变了。当他按下鼠标,画板记录的是当前的线宽。这就是线宽的丢失问题了。这里的问题原因是每次重新绘制时,无法取到上一次设置的笔的大小。 第一次用户进行绘制时,是选择了笔的大小的。但是绘制完毕之后,再次进行绘制时,此时笔的大小呢?
颜色和线宽会互相影响的问题解决
当用户按下鼠标时,获取的线宽和颜色是确定的,只是在绘制的过程中会出现丢失的情况。解决办法是使用全局变量存储按下鼠标时的线宽和大小,当用户自己绘制时,只取自己的存下的线宽和大小就行了。
注1:对于颜色,可以始终从取色板取值,也可以解决这个问题。因为取色板只是在本地进行改变,其他用户是无法影响它的。
注2:每次进行绘制时,都需要重新开始一条新的路径,即执行context.beginPath()
,否则设置颜色和大小不会生效。
用户id问题
因为每个的信息都需要携带一个id信息,所以这个id不能在前端生成,它必须是为唯一的。所以我的想法是将后台生成的uuid作为用户的唯一id(不过这样会不会有信息泄露的风险?)。这里采取的方式是用户默认使用id: "0"
,但是后台接收到这个数据时就将其设置为用户的uuid,再将其转发给其它客户端。
最终效果
注:这个龙字上面的黑点是Gif制作软件的问题。
动态
静态
附录 后端代码改动部分
注:新增加一个结构体Msg,修改了Read方法。
// 传输的信息格式定义
type Msg struct {
Id string `json:"id"` // 用户标识,也是数据的标识
Type uint8 `json:"type"` // 点的类型
X int `json:"x"` // x坐标
Y int `json:"y"` // y坐标
Color string `json:"color"` // 颜色
Size string `json:"size"` // 大小
}
// 读信息,从 websocket 连接直接读取数据
func (c *Client) Read(manager *Manager) {
defer func() {
WebsocketManager.UnRegister <- c
log.Printf("client [%s] disconnect", c.Id)
if err := c.Conn.Close(); err != nil {
log.Printf("client [%s] disconnect err: %s", c.Id, err)
}
}()
for {
msg := new(Msg)
err := c.Conn.ReadJSON(msg)
if err != nil {
log.Println(err)
break
}
msg.Id = c.Id // 给消息设置id,使用当前连接的uuid
log.Println(msg)
data, _ := json.Marshal(msg)
log.Printf("client [%s] receive message: %s", c.Id, string(data))
// 向广播消息写入数据
manager.BroadCastMessage <- &BroadCastMessageData{Id: c.Id, Message: data}
}
}
总结
好了,总算是结束了这几篇博客了。我发现最难的地方在于需要找别人进行测试,这太苦难了!虽然说只要思想不滑坡,方法总比困难多!我使用OTG线,给手机连上鼠标,然后笔记本使用鼠绘板操作,也是勉强可以进行测试的,但是这种方式太难受了,我不是很喜欢。这个东西还有很多值得完善的地方,不过我的工作就到这里为止了。
例如:
1、一段时间之后,前端的websocket连接就会自动断开了,这是因为没有做心跳检测来维持连接的活跃性。
2、每个人进行绘制时,没有显示它的标识,不容易进行区分。
它还有很多值得改进的地方呢,而且我的代码也没有经过优化,基本上只是逻辑通顺了。如果你感兴趣的话,可以尝试自己再去优化,也希望这个能对你有所启发!
更多推荐
所有评论(0)