当前位置: 首页 > news >正文

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

采用Html5的canvas做前端画画板,发送数据到后端Netty服务,实时转发笔迹数据,在线实时同步画笔轨迹

页面:

<!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"></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;});</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();}}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 数据结构-2.9.双链表
  • 科技引领未来生活——“光影漫游者”展览馆应用—轻空间
  • 每日学习一个数据结构-布隆过滤器Bloom Filter
  • 数据结构:二叉树(2)
  • Linux 清空redis缓存及查询key值
  • 修改 Visual Studio 的主题颜色、背景颜色、字体
  • 分布式计算技术是什么?在数据集成值得作用?
  • Java项目实战II基于Java+Spring Boot+MySQL的车辆管理系统(开发文档+源码+数据库)
  • Stable Cascade | ComfyUI API 工作流格式优化
  • 教你如何在微信小程序中轻松实现人脸识别功能
  • STM32——SPI
  • JVM基础篇学习笔记
  • Stable Diffusion不同部件拆分详解!
  • nethogs显示每个进程所使用的带宽
  • Git可视化工具和基础命令
  • 2017年终总结、随想
  • CSS魔法堂:Absolute Positioning就这个样
  • iOS | NSProxy
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • JavaScript设计模式系列一:工厂模式
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • Vue.js-Day01
  • 大主子表关联的性能优化方法
  • 构造函数(constructor)与原型链(prototype)关系
  • 基于 Babel 的 npm 包最小化设置
  • 设计模式(12)迭代器模式(讲解+应用)
  • 深度学习入门:10门免费线上课程推荐
  • 微信小程序上拉加载:onReachBottom详解+设置触发距离
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 学习JavaScript数据结构与算法 — 树
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • #define、const、typedef的差别
  • #window11设置系统变量#
  • #每日一题合集#牛客JZ23-JZ33
  • (13)Hive调优——动态分区导致的小文件问题
  • (javaweb)Http协议
  • (独孤九剑)--文件系统
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (牛客腾讯思维编程题)编码编码分组打印下标题目分析
  • (一)Kafka 安全之使用 SASL 进行身份验证 —— JAAS 配置、SASL 配置
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • *算法训练(leetcode)第四十天 | 647. 回文子串、516. 最长回文子序列
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .NET Core 发展历程和版本迭代
  • .net 使用$.ajax实现从前台调用后台方法(包含静态方法和非静态方法调用)
  • .net/c# memcached 获取所有缓存键(keys)
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .net快速开发框架源码分享
  • .NET文档生成工具ADB使用图文教程
  • /dev下添加设备节点的方法步骤(通过device_create)
  • @开发者,一文搞懂什么是 C# 计时器!
  • [ C++ ] STL---仿函数与priority_queue
  • [ 云计算 | AWS 实践 ] 基于 Amazon S3 协议搭建个人云存储服务
  • [@Controller]4 详解@ModelAttribute