Netty+HTML5+Canvas 网络画画板实时在线画画
采用Html5的canvas做前端画画板,发送数据到后端Netty服务,实时转发笔迹数据,在线实时同步画笔轨迹,单击绿色小方块,保存画板的图片
页面:
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>网络画画板</title>
</head>
<body>
<style>
#box1 {
width: 100px;
height: 100px;
background-color: green;
position: absolute;
}
</style>
<div id="box1" onclick="save()"></div>
<canvas id="canvas" style="background-color: yellow;"></canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 600;
let isDrawing = false;
let x = 0;
let y = 0;
let x1 = 0;
let y1 = 0;
var socket = new WebSocket("ws://localhost:9911/websocket");
socket.onopen = function(event) {
console.log("WebSocket opened: " + event);
};
socket.onmessage = function(event) {
//console.log("WebSocket message received: " + event.data);
var str = event.data.split("_");
if(str[0]==='B'){
switch (str[1]) {
case '37':
box1.style.left = str[2];
break;
case '39':
box1.style.left = str[2] ;
break;
case '38':
box1.style.top = str[2] ;
break;
case '40':
box1.style.top = str[2] ;
break;
}
}else if(str[0]==='mousedown'){
ctx.beginPath();
ctx.strokeStyle = "red";
ctx.lineWidth = 1;
ctx.moveTo(str[1], str[2]);
}else if(str[0]==='mousemove'){
ctx.lineTo(str[1], str[2]);
ctx.stroke();
}
};
socket.onclose = function(event) {
console.log("WebSocket closed: " + event);
};
function send() {
var message = document.getElementById("message").value;
socket.send(message);
}
document.addEventListener('keydown', function(event) {
if (event.keyCode === 13) {
send()
var txt = document.getElementById('message')
txt.value = ''
}
speed = 10
switch (event.keyCode) {
case 37:
box1.style.left = box1.offsetLeft - speed + "px";
socket.send("B_"+event.keyCode+"_"+box1.style.left)
break;
case 39:
box1.style.left = box1.offsetLeft + speed + "px";
socket.send("B_"+event.keyCode+"_"+box1.style.left)
break;
case 38:
box1.style.top = box1.offsetTop - speed + "px";
socket.send("B_"+event.keyCode+"_"+box1.style.top)
break;
case 40:
box1.style.top = box1.offsetTop + speed + "px";
socket.send("B_"+event.keyCode+"_"+box1.style.top)
break;
}
});
canvas.addEventListener("mousedown", (event) => {
// console.log(event);
x = event.layerX - canvas.getBoundingClientRect().left;
y = event.layerY - canvas.getBoundingClientRect().top;
ctx.beginPath();
ctx.strokeStyle = "red";
ctx.lineWidth = 1;
ctx.moveTo(x, y);
isDrawing = true;
socket.send("mousedown_"+x+"_"+y);
});
canvas.addEventListener("mousemove", (event) => {
//console.log(event);
//console.log(event.layerX, event.layerY);
if (isDrawing) {
x = event.layerX - canvas.getBoundingClientRect().left;
y = event.layerY - canvas.getBoundingClientRect().top;
ctx.lineTo(x, y);
ctx.stroke();
socket.send("mousemove_"+x+"_"+y);
}
});
canvas.addEventListener("mouseup", (event) => {
isDrawing = false;
});
canvas.addEventListener("mouseout", (event) => {
isDrawing = false;
});
function save(){
var link = document.createElement("a");
var imgData =canvas.toDataURL({format: 'png', quality:1, width:20000, height:4000});
var strDataURI = imgData.substr(22, imgData.length);
var blob = dataURLtoBlob(imgData);
var objurl = URL.createObjectURL(blob);
link.download = "grid1.png";
link.href = objurl;
link.click();
}
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
</script>
</body>
</html>
后端:
pom:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.nno</groupId>
<artifactId>nno</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>nno</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.18.Final</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
</dependencies>
</project>
类:
package org.nno;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import java.util.HashSet;
import java.util.Set;
public class WebSocketServer {
private final int port;
public WebSocketServer(int port) {
this.port = port;
}
Set m = new HashSet();
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(65536));
p.addLast(new WebSocketServerProtocolHandler("/websocket"));
p.addLast(new WebSocketServerHandler(m));
}
});
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 9911;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}
new WebSocketServer(port).run();
}
}
package org.nno;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import java.util.*;
public class WebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
Set m = new HashSet<>();
public WebSocketServerHandler(Set m) {
this.m = m;
}
@Override
public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
// 处理消息
// System.out.println("Received message: " + msg.text());
Iterator<Channel> iterator = m.iterator();
while(iterator.hasNext()){
iterator.next().writeAndFlush(new TextWebSocketFrame(msg.text()));
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 添加连接
// System.out.println("Client connected: " + ctx.channel());
m.add(ctx.channel());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// 断开连接
// System.out.println("Client disconnected: " + ctx.channel());
m.remove(ctx.channel());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 异常处理
cause.printStackTrace();
ctx.close();
}
}