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

ppt模板网站源码苏州网站开发公司排名

ppt模板网站源码,苏州网站开发公司排名,天津网站排名提升多少钱,商业网站的后缀基于 yjs 实现实时在线多人协作的绘画功能 支持多客户端实时共享编辑自动同步#xff0c;离线支持自动合并#xff0c;自动冲突处理 1. 客户端代码#xff08;基于Vue3#xff09; 实现绘画功能 templatediv style{width: 100vw; height: 100vh; over…基于 yjs 实现实时在线多人协作的绘画功能 支持多客户端实时共享编辑自动同步离线支持自动合并自动冲突处理 1. 客户端代码基于Vue3 实现绘画功能 templatediv style{width: 100vw; height: 100vh; overflow: hidden;}canvas refcanvasRef style{border: solid 1px red;} mousedownstartDrawing mousemovedrawmouseupstopDrawing mouseleavestopDrawing/canvas/divdiv styleposition: absolute; bottom: 10px; display: flex; justify-content: center; height: 40px; width: 100vw;div stylewidth: 100px; height: 40px; display: flex; align-items: center; justify-content: center; color: white;:style{ backgroundColor: color }span当前颜色/span/divButton stylewidth: 100px; height: 40px; margin-left: 10px; clickswitchMode(DrawType.Point)画点/ButtonButton stylewidth: 100px; height: 40px; margin-left: 10px; clickswitchMode(DrawType.Line)直线/ButtonButton stylewidth: 100px; height: 40px; margin-left: 10px; clickswitchMode(DrawType.Draw)涂鸦/ButtonButton stylewidth: 100px; height: 40px; margin-left: 10px; clickclearCanvas清除/Button/div /templatescript setup langts import { ref, onMounted } from vue; import { Button, Modal, Input } from ant-design-vue; import * as Y from yjs; import { WebsocketProvider } from y-websocket; import { v4 as uuidv4 } from uuid;const canvasRef refnull | HTMLCanvasElement(null); const ctx refCanvasRenderingContext2D | null(null); const drawing ref(false); const color refstring(black);class Point {x: number 0.0;y: number 0.0; }enum DrawType {None,Point,Line,Draw, }const colors [#FF5733, #33FF57, #5733FF, #FF33A2, #A2FF33,#33A2FF, #FF33C2, #C2FF33, #33C2FF, #FF3362,#6233FF, #FF336B, #6BFF33, #33FFA8, #A833FF,#33FFAA, #AA33FF, #FFAA33, #33FF8C, #8C33FF ];// 随机选择一个颜色 function getRandomColor() {const randomIndex Math.floor(Math.random() * colors.length);return colors[randomIndex]; }class DrawElementProp {color: string black; }class DrawElement {id: string ;version: string ;type: DrawType DrawType.None;geometry: Point[] [];properties: DrawElementProp new DrawElementProp(); }// 选择的绘画模式 const drawMode refDrawType(DrawType.Draw); // 定义变量来跟踪第一个点的坐标和鼠标是否按下 const point refPoint | null(null);// 创建 ydoc, websocketProvider const ydoc new Y.Doc();// 创建一个 Yjs Map用于存储绘图数据 const drawingData ydoc.getMapDrawElement(drawingData);drawingData.observe(event {if (ctx.value canvasRef.value) {const context ctx.value!// 清空 Canvascontext.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);// 遍历绘图数据绘制点、路径等drawingData.forEach((data: DrawElement) {if (data.type DrawType.Point) {context.fillStyle data.properties.color; // 设置点的填充颜色context.strokeStyle data.properties.color; // 设置点的边框颜色context.beginPath();context.moveTo(data.geometry[0].x, data.geometry[0].y);context.arc(data.geometry[0].x, data.geometry[0].y, 2.5, 0, Math.PI * 2); // 创建一个圆形路径context.fill(); // 填充路径形成圆点context.closePath();} else if (data.type DrawType.Line) {context.fillStyle data.properties.color; // 设置点的填充颜色context.strokeStyle data.properties.color; // 设置点的边框颜色context.beginPath();// 遍历所有点data.geometry.forEach((p: Point, index: number) {if (index 0) {context.moveTo(p.x, p.y);context.fillRect(p.x, p.y, 5, 5);} else {context.lineTo(p.x, p.y);context.stroke();context.fillRect(p.x, p.y, 5, 5);}})} else if (data.type DrawType.Draw) {context.fillStyle data.properties.color; // 设置点的填充颜色context.strokeStyle data.properties.color; // 设置点的边框颜色context.beginPath();// 遍历所有点data.geometry.forEach((p: Point, index: number) {if (index 0) {context.moveTo(p.x, p.y);} else {context.lineTo(p.x, p.y);context.stroke();}})} else {console.log(Invalid draw data, data)}})} })const websocketProvider new WebsocketProvider(ws://localhost:8080/ws, demo, ydoc )onMounted(() {if (canvasRef.value) {// 随机选择一种颜色color.value getRandomColor()canvasRef.value.height window.innerHeight - 10;canvasRef.value.width window.innerWidth;const context canvasRef.value.getContext(2d);if (context) {ctx.value context;context.lineWidth 5;context.fillStyle color.value; // 设置点的填充颜色context.strokeStyle color.value; // 设置点的边框颜色context.lineJoin round;}}window.addEventListener(keydown, handleKeyDown); });const handleSaveUserName () {if (userName.value) {modalOpen.value false;} }const handleKeyDown (event: KeyboardEvent) {if (event.key Escape) {// 重置编号if (currentID.value) {currentID.value ;}// 结束路径和绘画if (drawing.value ctx.value) {ctx.value.closePath();drawing.value false;}} }const switchMode (mode: DrawType) {// 重置状态currentID.value ;drawing.value false;drawMode.value mode;point.value null }// 记录当前路径的编号 const currentID refstring();const startDrawing (e: any) {// 获取当前时间的秒级时间戳const timestampInSeconds Math.floor(Date.now() / 1000);// 将秒级时间戳转换为字符串const version timestampInSeconds.toString();if (ctx.value) {if (drawMode.value DrawType.Point) {// 分配编号currentID.value uuidv4();let point: DrawElement {id: currentID.value,version: version,type: DrawType.Point,geometry: [{ x: e.clientX, y: e.clientY }],properties: { color: color.value }}drawingData.set(currentID.value, point);// 重置编号currentID.value return}if (drawMode.value DrawType.Line) {// 分配编号if (currentID.value ) {currentID.value uuidv4();}// 没有正在绘画if (!drawing.value) {// 开始绘画drawing.value true;}// 获取当前线的信息如果没有则创建let line: DrawElement | undefined drawingData.get(currentID.value)if (line) {line.version version;line.geometry.push({ x: e.clientX, y: e.clientY });} else {line {id: currentID.value,version: version,type: DrawType.Line,geometry: [{ x: e.clientX, y: e.clientY }],properties: { color: color.value }}}drawingData.set(currentID.value, line);return}if (drawMode.value DrawType.Draw) {// 分配编号if (currentID.value ) {currentID.value uuidv4();let path: DrawElement {id: currentID.value,version: version,type: DrawType.Draw,geometry: [{ x: e.clientX, y: e.clientY }],properties: { color: color.value }}drawingData.set(currentID.value, path);}// 没有正在绘画if (!drawing.value) {// 开始绘画drawing.value true;}}} };const draw (e: any) {if (drawing.value ctx.value) {if (drawMode.value DrawType.Draw) {// 获取当前线的信息如果没有则创建let path: DrawElement | undefined drawingData.get(currentID.value)if (path) {path.geometry.push({ x: e.clientX, y: e.clientY });drawingData.set(currentID.value, path);return}console.log(error: not found path, currentID.value)}} };const stopDrawing () {if (drawing.value ctx.value) {if (drawMode.value DrawType.Draw) {// 鼠标放开时关闭当前路径绘画currentID.value ;drawing.value false;}} };const clearCanvas () {if (canvasRef.value ctx.value) {ctx.value.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height);drawingData.clear();} }; /script 2. 服务端代码 基于 yjs 的多人协助其实只需要前端使用 y-webtrc 也可以实现数据共享但是为了增加一些功能如权限控制、数据库存储等需要使用服务端不考虑复杂功能我们使用 websocket 进行客户端之间的通信所以服务端也很简单实现了 websocket 服务端的功能即可 可以使用 yjs 推荐的 y-websocket 的 nodejs 服务 HOSTlocalhost PORT8080 npx y-websocket也可以自己实现一个 websocket 服务端这里选择用 golang 实现一个 // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file.package mainimport (net/httpgithub.com/olahol/melody )func main() {m : melody.New()m.Config.MessageBufferSize 65536m.Config.MaxMessageSize 65536m.Upgrader.CheckOrigin func(r *http.Request) bool { return true }http.HandleFunc(/ws/demo, func(w http.ResponseWriter, r *http.Request) {m.HandleRequest(w, r)})// 不重要m.HandleConnect(func(session *melody.Session) {println(connect)})// 不重要m.HandleDisconnect(func(session *melody.Session) {println(disconnect)})// 不重要m.HandleClose(func(session *melody.Session, i int, s string) error {println(close)return nil})// 不重要m.HandleError(func(session *melody.Session, err error) {println(error, err.Error())})// 不重要m.HandleMessage(func(s *melody.Session, msg []byte) {m.Broadcast(msg)})// 主要内容对 yjs doc 的改动内容进行广播到其他客户端m.HandleMessageBinary(func(s *melody.Session, msg []byte) {m.BroadcastBinary(msg)})http.ListenAndServe(:8080, nil) } 3. 特殊的 nodejs 客户端用于保存数据 yjs 在客户端上进行文档冲突处理以及合并每个客户端都维护着自己的文档为了使数据能够持久化到文件或者数据库中需要使用一个客户端作为基准并且这个客户端对文档应该是只读不改的运行在服务器上基于以上考量我们选择使用 nodejs 实现一个客户端运行在服务器上如果选用golang的话没有 yjs 实现的方法可以解析 ydoc 的数据 nodejs 客户端只需要连接上 y-websocket 并且当文档更新时保存数据 const fs require(fs); const Y require(yjs); const { WebsocketProvider } require(y-websocket); const WebSocket require(websocket).w3cwebsocket;// 创建 Yjs 文档 const ydoc new Y.Doc();const websocketProvider new WebsocketProvider(ws://localhost:8080/ws, demo, ydoc, {WebSocketPolyfill: WebSocket, })const drawingData ydoc.getMap(drawingData);// 当文档发生更改时将更改内容打印出来 ydoc.on(update, () {console.log(Document updated, ydoc.clientID);const document [];drawingData.forEach((data) {document.push(data)})// 要写入的文件路径const filePath doc/data.json;const fileContent JSON.stringify(document);// 使用 fs.writeFile 方法写入文件fs.writeFile(filePath, fileContent, (err) {if (err) {console.error(save error, err);} else {console.log(document saved);}}); });
http://www.hkea.cn/news/14404862/

相关文章:

  • 网站的 联系我们怎么做wordpress花园主题
  • 网站修改图片链接连云港吧
  • 郑州建站模板厂家互站网源码网站
  • 上海网站设计大连网站规划建设实训报告
  • 做的比较唯美的网站ueditor wordpress 插件
  • 设计网站printerest商城手机网站开发
  • 学校营销型网站网站有什么到期
  • 阿里巴巴做网站的德国和俄罗斯和做视频网站
  • 海尔电子商务网站建设预算旅游电子商务项目计划书
  • 做一个什么网站好seo文章优化方法
  • wordpress快站怎么样百度集团官网
  • easyui做门户网站服装设计好找工作吗
  • win7 iis架设网站教务管理系统哪个好
  • 做网站 有哪些问题同ip下网站
  • 网站开发自学难吗广告公司图片大全
  • tiktok官方网站入口ftp如何上传网站
  • 自己做网站引用别人的电影centos 网站开发工具
  • 网页建站要多久wordpress 命令插件
  • 手机网站和电脑网站样式的区别wordpress的DUX主题
  • 网站开发费怎么做会计分录网页设计技术学什么
  • 怎么建设个人网站黑客收徒网站建设
  • 如何建设小说网站搜索引擎优化是什么?
  • 阿里云网站建设的功能培训美工设计师
  • 天津网站设计建设免费注册域名方法
  • js网站计数器代码百度域名的书写
  • 做外贸英语要什么网站北京网站建设 shwl
  • 昭通网站seo网站建设案例行情
  • 房地产公司网站模板什么主题的网站容易做
  • 网页模板免费源码网站seo诊断报告例子
  • 淘宝联盟网站推广怎么做wordpress标签使用方法