通过前面的几篇博客,基本上已经可以实现数据在网络上的传递和正确的还原了。但是现在还留有一些细节上的问题亟待解决,所以在最后一篇博客,我们来进行一些收尾的工作!

目前待解决的问题?

  1. 多人可以同时进行绘制,但是线条的样式变得离散了,不再连续,很难看。
  2. 多人同时进行绘制时,线条的颜色和粗细会进行干扰。

我们先来解决第一个问题吧,因为它比较简单。

线条离散问题

改进方式:
在绘制前,设置context的lineCaplineJoin样式为“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、每个人进行绘制时,没有显示它的标识,不容易进行区分。

它还有很多值得改进的地方呢,而且我的代码也没有经过优化,基本上只是逻辑通顺了。如果你感兴趣的话,可以尝试自己再去优化,也希望这个能对你有所启发!

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐