说明:
我正在建立一个网站,其主要功能之一是白板。您可以与另一个用户打开一个会话,然后在他将看到的板上绘制任何内容,反之亦然。
使用HTML和javascript实现,点对点。
问题:
现在有两件事可以在你的棋盘上触发绘图 - 你在会话中画出一些东西或你的伴侣画出一些东西。 (均触发绘图事件)。
当你画出一些东西时,问题就开始出现了。 (图纸是混合的,我的输入和我的伙伴收到的一个是混合的,不能并行处理两个图纸输入)
解决方案:(?)
多线程可以解决它。在单独的线程中处理合作伙伴的绘图。但是,JavaScript不是多线程的。 我读到了使用'worker'(HTML 5)的选项,但如果我理解正确,它在处理主线程中的变量时非常有限,并且不能影响HTML(改变屏幕上显示的内容)。
有什么想法吗?
答案 0 :(得分:1)
多线程无法为您解决此问题。 Web Workers已经如上所述,受限并且在这种情况下不会增加任何好处,因为这需要访问DOM等(您可以使用它们来使用可转移对象处理像素,即类型化数组,但是你必须在处理之后将它们传回你的主JS线程,这样你就可以了。)
好消息,你可以同时画两幅不同的画作。我假设您使用canvas来实现这一目标。
只需创建两个叠加的画布,然后将您的本地鼠标/触摸动作提供给其中一个画布,同时通过Web套接字将数据提供给另一个画布。
另一种方法是以原子方式绘制每个段 - 对于每个鼠标移动以及通过表示移动的Web套接字接收的每个数据(您可能需要在此处迭代),例如:
ctx.beginPath(); /// reset path for this segment
ctx.moveTo(oldX, oldY); /// of current client/user
ctx.lineTo(x, y); /// current position
ctx.strokeStyle = currentColor; /// the one drawing this line
ctx.stroke();
(oldX / Y将通过鼠标按下事件首次初始化。)
以分段的方式执行此操作允许两者同时绘制,尽管它的性能更高一些。就个人而言,如果行的顺序不重要,我会选择分层画布解决方案。由于您的解决方案将使用事件,因此它将是异步的并且将处理在此处应该正常工作的事件队列。
只需设置一些数组来保存每个用户的数据(旧位置,样式等),这样就可以为当前用户/客户端使用ID /索引。
一些伪代码,用于简化分段方法的概述:
var oldX = [],
oldY = [],
currentColor = [], ...;
...set up clients, arrays, mousedown/up etc.
canvas.onmousemove = function(e) {
var pos = getMousePos ...
if (isDrawing) drawSegment(0, pos.x, pos.y); /// f.ex. client 0 = local
}
function handleSocketData() {
... get x/y from data stream
drawSegment(1, x, y); /// f.ex. client 1 = web socket
}
function drawSegment(client, x, y) {
ctx.beginPath();
ctx.moveTo(oldX[client], oldY[client]);
ctx.lineTo(x, y);
ctx.strokeStyle = currentColor[client];
ctx.stroke();
oldX[client] = x;
oldY[client] = y;
}
这当然是简化的。您需要将每个点存储在一个点数组中,其具有与绘制时相同的状态细节,以便能够重绘画布(如果已清除),处理套接字上的多个段等等,但我认为它给人的印象是如何实现这种方法的核心原则。
希望这有帮助!
答案 1 :(得分:0)
在这种情况下,“竞争条件”不与线程相关或通过线程解决 - 它实际上只会让线程变得更复杂。
相反,在实现绘制哪些行时,需要定义和遵循序列化计划/顺序,以及何时 - 例如谁的线条“排在首位”?它是按段还是按路径?通过下笔(第一个)或上笔(上一个)优先级?这可以在JavaScript使用的异步模型的上下文中简单地处理。
最简单的方法是根据客户收到的顺序绘制线条;如果需要一致的视图,则算法/协议需要引入同步,例如共享计数器或“时间戳”,可能是服务器在收到更新时注入的。
不同对等体的状态可以简单地保存在对象中(例如,键入对等体的地图),并且不难设置或维护。
Web Workers是[浏览器] JavaScript必须线程化的最接近的事物。
Web Workers更类似于BackgroundWorkers,因为它经常用于UI框架,例如Swing in Java或.NET WebForms。也就是说,后台任务 - 无论是Web Worker还是后台工作者 - 不允许修改UI / HTML,但必须触发某种形式的回调,然后在“正确的线程”上处理。
在许多情况下,这实际上比非限制性线程和跨线程访问更好 :没有一致性或原子性担忧!
答案 2 :(得分:0)
在这种情况下,我不认为需要多个线程。您可以在单线程中完成所有绘图。假设你有一个函数在两点之间画一条线。您可以在本地绘制事件(用户自己绘制时)以及收到合作伙伴数据时调用此函数。您甚至可以在短暂延迟后为合作伙伴绘图制作动画。