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

交互式网站设计怎么给网站设置搜索关键词 wordpress

交互式网站设计,怎么给网站设置搜索关键词 wordpress,文化传播公司 网站设计,做网站的挣钱么基于 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/14480349/

相关文章:

  • 自己做博客网站包装设计专业是学什么的
  • 上海网站搭建公司营销网站建设费用
  • 网站关键词锚文本指向网站怎么关闭
  • 做图库网站需要多少钱上海企业网站制作报价
  • 外流网站建设南昌大型网站制作
  • 优秀的设计网站推荐食品行业网站源码
  • 内容分享网站设计一个网站如何做cdn加速
  • 黑色门户网站源码做网站推广好做吗
  • 如何用国外网站做头条广东网络建设有限公司
  • 电商平台网站设计公司有专业做网站
  • 域名价格查询网站建筑工程分为哪几类
  • 门户网站开发费怎做账知识付费小程序源码
  • c 教学网站开发农村电商平台开发
  • 怎么做招投标网站网站建设询价单
  • 淘宝网站做淘宝客免费空间建网站
  • 网站域名怎么做厂家网站怎么做
  • 基于ssh框架的网站开发流程图网站备案需要拍照
  • 西宁网站怎么做seo科技有限公司注册条件
  • 免费建站系统wordpress临沂做企业网站的公司
  • 网站建设基地貴阳建设银行网站
  • 网站资源规划怎么写关于网站建设维护的创业计划书
  • 绍兴网站建设方案托管驾校网站建设费用
  • 做网站论文长沙市建站
  • 国外网站建设的步骤wordpress是响应式吗
  • 免费网站软件大全西安seo阳建
  • 网站建设会议议程网站优化 无需定金
  • 制作网站的步骤是什么代码做网站
  • 徐州服饰网站建设想做个app软件需要什么条件
  • 做网站的排名专门做简历的网站软件
  • 网站建设所采用的技术网站开发设计工程师职责简介