用SignalR实现的共享画板例子
使用HTML5的canvas画布功能,在页面进行绘画,然后通过SignalR将画布的每个点的颜色提交到服务端,服务端同时将这些画布的信息推送到其他客户端,实现共享同一个画板的功能
类似下图,在某一个浏览器进行绘画,其他浏览器同步显示内容,并且页面刷新或者首次加载还能显示之前的绘画内容(站点不重启的情况下)
实现过程
一、服务端
服务端的代码主要功能是接收客户端发送过来的绘画坐标点和坐标点的颜色,同时将新的坐标点信息推送给客户端,最后服务端还会保存这些绘画坐标点信息到内存中,这样客户端刷新或者首次进入就能看到之前的绘画信息。
step1:
创建一个Empty类型的ASP.NET 4.5的站点
step2:
引入SignalR的nuget包,将自动引入相关的关联包
Install-Package Microsoft.AspNet.SignalR
step3:
新建一个SignalR Hub Class
实现代码如下:
public class DrawingBoard : Hub { private const int BoardWidth = 300; private const int BoardHeight = 300; private static int[,] _pointBuffer = GetEmptyPointBuffer(); public Task DrawPoint(int x, int y) { if (x < 0) { x = 0; } if (x >= BoardWidth) { x = BoardWidth - 1; } if (y <0) { y = 0; } if (y >= BoardHeight) { y = BoardHeight - 1; } int color = 0; int.TryParse(Clients.Caller.color, out color); _pointBuffer[x, y] = color; return Clients.Others.drawPoint(x, y, Clients.Caller.color); } public Task Clear() { _pointBuffer = GetEmptyPointBuffer(); return Clients.Others.clear(); } public override Task OnConnected() { return Clients.Caller.update(_pointBuffer); } private static int[,] GetEmptyPointBuffer() { var buffer = new int[BoardWidth, BoardWidth]; return buffer; } }
step4:
新建一个startup类
public class Startup { public void Configuration(IAppBuilder app) { app.MapSignalR(); } }
到此完成服务端的功能实现
二、客户端
step1:
客户端的html页面只需要定义一个canvas元素和一个清除画布的按钮
<!DOCTYPE html> <html> <head> <title>在线画板</title> <meta charset="utf-8" /> <style> div{ margin:3px; } canvas{ border:2px solid #808080; cursor:default; } </style> </head> <body> <div> <div> <label for="color">Color:</label> <select id="color"></select> </div> <canvas id="canvas" width="300" height="300"></canvas> <div> <button id="clear">Clear canvas</button> </div> </div> <script src="/Scripts/jquery-1.6.4.min.js"></script> <script src="/Scripts/jquery.signalR-2.2.0.min.js"></script> <script src="/signalr/js"></script> <script src="/Scripts/drawingboard.js"></script> </body> </html>
step2:
在scripts目录创建drawingboard.js文件,实现主要的画布操作和SingalR事件绑定。
主要是绑定了canvas的mousemove事件,然后再mousemove事件中实现绘画,同时将绘画的信息发送服务器,客户端同时接收服务端推送的绘画信息,实时提现在本地的画布中。
客户端连接成功之后,会获得从服务端推送的历史绘画信息,实现记载之前画布的效果
$(function () { // 画布定义开始 var colors = ["black", "red", "green", "blue", "yellow", "magenta", "white"]; var canvas = $("#canvas"); var colorElement = $("#color"); for (var i = 0; i < colors.length; i++) { colorElement.append("<option value='" + (i + 1) + "'>" + colors[i] + "</option>"); } var buttonPressed = false; canvas .mousedown(function () { buttonPressed = true; }) .mouseup(function () { buttonPressed = false; }) .mousemove(function (e) { if (buttonPressed) { setPoint(e.offsetX, e.offsetY, colorElement.val()); } }); var ctx = canvas[0].getContext("2d"); function setPoint(x, y, color) { ctx.fillStyle = colors[color - 1]; ctx.beginPath(); ctx.arc(x, y, 2, 0, Math.PI * 2); ctx.fill(); } function clearPoints() { ctx.clearRect(0, 0, canvas.width(), canvas.height()); } $("#clear").click(function () { clearPoints(); }); // 画布定义结束 // SignalR 客户端代码 var hub = $.connection.drawingBoard; hub.state.color = colorElement.val(); var connected = false; colorElement.change(function () { hub.state.color = $(this).val(); }); canvas.mousemove(function (e) { if (buttonPressed && connected) { hub.server.drawPoint(Math.round(e.offsetX), Math.round(e.offsetY)); } }); $("#clear").click(function () { if (connected) { hub.server.clear(); } }); // 定义客户端方法 hub.client.clear = function () { clearPoints(); }; hub.client.drawPoint = function (x, y, color) { setPoint(x, y, color); }; hub.client.update = function (points) { if (!points) return; var width = canvas.width(); var height = canvas.height(); for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { var color = points[x][y]; if (color > 0) { setPoint(x, y, color); } } } }; $.connection.hub.start().done(function () { connected = true; }); })
完整代码下载