Netty+HTML5+Canvas 网络画画板实时在线画画

devtools/2024/9/22 11:59:17/

采用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 = 10switch (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>() {@Overridepublic 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;}@Overridepublic 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()));}}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {// 添加连接
//        System.out.println("Client connected: " + ctx.channel());m.add(ctx.channel());}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {// 断开连接
//        System.out.println("Client disconnected: " + ctx.channel());m.remove(ctx.channel());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {// 异常处理cause.printStackTrace();ctx.close();}}


http://www.ppmy.cn/devtools/115444.html

相关文章

怎么让Nginx可以访问某一IP的每个后台controller接口

http { upstream backend { server 192.168.1.10; # 后端服务器IP } server { listen 80; location /controller1/ { ##proxy_pass http://localhost:801; proxy_pass http://backend/controller1/; # 后端controller1…

工业一体机在汽车零部件工厂ESOP系统中的关键作用

在当今竞争激烈的汽车市场中&#xff0c;汽车零部件工厂的高效生产和严格质量控制至关重要。而工业一体机在汽车零部件工厂的 ESOP&#xff08;电子标准化作业程序&#xff09;系统中发挥着关键作用。 一、汽车零部件工厂面临的挑战 汽车零部件的生产过程复杂且要求严格&#…

C++ 知识要点:其它

八. 其它 调试程序的方法&#xff1f; gdb 遇到 coredump 要怎么调试&#xff1f;模板的用法与适用场景用过 C11&#xff1f; 新特性&#xff1f;auto,decltype、explicit、lambda、final 函数调用的压栈过程sizeof 和 strlen 的区别&#xff1f; 运算符与函数计算的对象编译时…

鸿蒙 ArkUI组件二

ArkUI组件&#xff08;续&#xff09; 文本组件 在HarmonyOS中&#xff0c;Text/Span组件是文本控件中的一个关键部分。Text控件可以用来显示文本内容&#xff0c;而Span只能作为Text组件的子组件显示文本内容。 Text/Span组件的用法非常简单和直观。我们可以通过Text组件来显…

【深度学习】聊一聊正则化

在机器学习中&#xff0c;正则化是一种常用的技术&#xff0c;用于控制模型的复杂度&#xff0c;减少过拟合的风险。它通过在损失函数中引入额外的项来对模型的参数进行约束或惩罚&#xff0c;使模型更加简单、平滑或稀疏。我们在实际应用中&#xff0c;经常使用的是L1和L2正则…

使用dom-to-image截图html区域为一张图

第一步安装&#xff1a;npm i dom-to-image 第二步引入&#xff1a;import domToImage from dom-to-image; 第三步截图&#xff1a; // 截图 function screenshot() {return new Promise((resolve, reject) > {const images document.querySelectorAll(.isCrossOrigin); /…

【Linux学习】基本指令其一

命令行界面 命令行终端是一个用户界面&#xff0c;允许用户通过输入文本命令与计算机系统进行交互。 比如Windows下&#xff0c; 键入winR&#xff0c;然后输入cmd&#xff0c;就可以输入文本指令与操作系统交互了。 Windows有另一个命令行界面Powershell,它的功能比cmd更强大…

一个基于 laravel 和 amis 开发的后台框架, 友好的组件使用体验,可轻松实现复杂页面(附源码)

前言 随着互联网应用的发展&#xff0c;后台管理系统的复杂度不断增加&#xff0c;对于开发者而言&#xff0c;既要系统的功能完备&#xff0c;又要追求开发效率的提升。然而&#xff0c;传统的开发方式往往会导致大量的重复劳动&#xff0c;尤其是在构建复杂的管理页面时。有…