canvas+fabric实现时间刻度尺+长方形数据展示

前言

我们前面实现了时间刻度尺,现在在时间刻度尺里面画一个长方形,长方形里面有数据展示。

效果

实现

1.先实现时间刻度尺

2.鼠标移动、按下事件监听并画出对应效果

3.在刻度尺里面画对应的长方形数据展示

<template><div><canvas id="rulerCanvas" width="1200" height="400"></canvas><div style="margin-top: 20px"><el-button type="primary" @click="test0">添加空标记</el-button><el-button type="primary" @click="test1">添加锅</el-button><el-button type="primary" @click="test2">添加用料</el-button><el-button type="primary" @click="test3">添加菜盒</el-button><el-button type="primary" @click="test4">添加结束录制</el-button><el-button type="primary" @click="test5" plain>清空全部</el-button></div><div style="margin-top: 20px"><span style="margin-right: 6px">开启鼠标弹框</span><el-switch v-model="value1" /></div><el-dialog v-model="dialogVisible" title="添加调料" width="500"><div><el-input v-model="input1" style="width: 240px" /></div><div style="margin-top: 10px"><el-input v-model="input2" style="width: 240px" /></div><template #footer><div><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="dialogConfirm">确认</el-button></div></template></el-dialog></div>
</template>
<script setup>
import * as fabric  from 'fabric';
import {ref, onMounted} from 'vue';const value1 = ref(false);
const dialogVisible = ref(false);
const input1 = ref("测试商品名称");
const input2 = ref("");
const dialogConfirm = () => {dialogVisible.value = false;console.log("====添加====")
};let left = 40;const test0 = () => {draw0();
};
const test1 = () => {draw1()
};
const test2 = () => {draw();
};
const test3 = () => {draw();
};
const test4 = () => {draw4();
};
const test5 = () => {console.log("清空全部");// canvas.value.clear();
};const canvas = ref(null);onMounted(() => {drawRuler();
});// const onClickItem = ref(false);const onMouseDown = (options) => {if (value1.value) {console.log("====onMouseMove====", options);let startNumber = options.pointer.x - 40;let timeNumber = parseInt(startNumber / 20);input2.value = timeToStr(timeNumber);dialogVisible.value = true;}
};let movDummyLine = null;
let movDummyLineText = null;let group = null;
let dummyLineText = null;// 空标记
const draw0 = () => {group = new fabric.Group([], {left: left,top: 40,width: 150,height: 400,selectable: true,lockMovementY: true,// 添加以下属性来隐藏边界框和控制点hasBorders: false,hasControls: false,hasRotatingPoint: false,});left = left + 150 + 20;group.on('selected', (e) => {console.log('组被选中', e);});// 为组添加移动事件监听器group.on('moving', () => {if (group.left >= 40) {let startNumber = group.left - 40 + 20;let timeNumber = parseInt(startNumber / 20);dummyLineText.set('text', timeToStr(timeNumber));}});const rect = new fabric.Rect({left: 0,top: 0,width: 150,height: 400,fill: '#d8f2ed',selectable: false,lockMovementY: true,}).set({left: group.left - 3, top: group.top});group.add(rect);// 添加虚线const dummyLine = new fabric.Line([0, 800, 1, 0], {stroke: 'red',strokeDashArray: [5, 5],strokeWidth: 1,selectable: false,}).set({ left: group.left, top: group.top});group.add(dummyLine);const line = new fabric.Line([0, 800, 2, 0], {stroke: '#3cbca3',strokeWidth: 2,selectable: true,lockMovementY: true,}).set({ left: group.left, top: group.top });group.add(line);const titleText = new fabric.Text("空标记", {fontSize: 12,fill: 'black',selectable: false,textAlign: 'center',}).set({ left: group.left + 4, top: group.top + 4 });group.add(titleText);// 刻度时间dummyLineText = new fabric.Text("00:00", {fontSize: 12,fill: 'black',selectable: false,textAlign: 'center',}).set({ left: group.left - 10, top: group.top - 20});group.add(dummyLineText);// 将组添加到画布上canvas.value.add(group);// 激活组的选择功能(选中该分组)// canvas.value.setActiveObject(group);
};// 锅设置
const draw1 = () => {group = new fabric.Group([], {left: left,top: 220,selectable: true,lockMovementY: true,// 添加以下属性来隐藏边界框和控制点hasBorders: false,hasControls: false,hasRotatingPoint: false,});left = left + 150 + 20;group.on('selected', (e) => {console.log('组被选中', e);});// 为组添加移动事件监听器group.on('moving', () => {if (group.left >= 40) {let startNumber = group.left - 40 + 20;let timeNumber = parseInt(startNumber / 20);dummyLineText.set('text', timeToStr(timeNumber));}});const rect = new fabric.Rect({left: 0,top: 0,width: 150,height: 180,fill: '#fff0e7',selectable: false,lockMovementY: true,}).set({left: group.left - 3, top: group.top});group.add(rect);const line = new fabric.Line([0, 180, 2, 0], {stroke: '#fc9153',strokeWidth: 2,selectable: true,lockMovementY: true,}).set({ left: group.left, top: group.top });group.add(line);// 添加虚线const dummyLine = new fabric.Line([0, 800, 1, 0], {stroke: 'red',strokeDashArray: [5, 5],strokeWidth: 1,selectable: false,}).set({ left: group.left, top: group.top});group.add(dummyLine);const titleText = new fabric.Text("锅设置为xxxx", {fontSize: 12,fill: 'black',selectable: false,textAlign: 'center',}).set({ left: group.left + 4, top: group.top + 4 });group.add(titleText);// 创建灰色背景的小矩形const textBackground = new fabric.Rect({left: group.left + 10,top: group.top + 20,width: 70,height: 35,fill: '#fbfbfb',selectable: false,rx: 4,});group.add(textBackground);const text = new fabric.Text('测试文字', {left: group.left + 10,top: group.top + 20,fontSize: 12,fill: 'black',textAlign: 'center',selectable: false,});text.set({top: text.top + textBackground.height / 2 - text.height / 2,left: text.left + textBackground.width / 2 - text.width / 2,});group.add(text);// 刻度时间dummyLineText = new fabric.Text("00:00", {fontSize: 12,fill: 'black',selectable: false,textAlign: 'center',}).set({ left: group.left - 10, top: group.top - 20});group.add(dummyLineText);// 将组添加到画布上canvas.value.add(group);// 激活组的选择功能(选中该分组)// canvas.value.setActiveObject(group);
};// 用料、菜盒
const draw = () => {group = new fabric.Group([], {left: left,top: 40,width: 150,height: 400,selectable: true,lockMovementY: true,// 添加以下属性来隐藏边界框和控制点hasBorders: false,hasControls: false,hasRotatingPoint: false,});left = left + 150 + 20;group.on('selected', (e) => {console.log('组被选中', e);});// 为组添加移动事件监听器group.on('moving', () => {if (group.left >= 40) {let startNumber = group.left - 40 + 20;let timeNumber = parseInt(startNumber / 20);dummyLineText.set('text', timeToStr(timeNumber));}});const rect = new fabric.Rect({left: 0,top: 0,width: 150,height: 400,fill: '#e1efff',selectable: false,lockMovementY: true,}).set({left: group.left - 3, top: group.top});group.add(rect);// 添加虚线const dummyLine = new fabric.Line([0, 800, 1, 0], {stroke: 'red',strokeDashArray: [5, 5],strokeWidth: 1,selectable: false,}).set({ left: group.left, top: group.top});group.add(dummyLine);const line = new fabric.Line([0, 800, 2, 0], {stroke: '#288bfd',strokeWidth: 2,selectable: true,lockMovementY: true,}).set({ left: group.left, top: group.top });group.add(line);const titleText = new fabric.Text("自动投放猪油11g", {fontSize: 12,fill: 'black',selectable: false,textAlign: 'center',}).set({ left: group.left + 4, top: group.top + 4 });group.add(titleText);// 创建灰色背景的小矩形const textBackground = new fabric.Rect({left: group.left + 10,top: group.top + 20,width: 70,height: 35,fill: '#fbfbfb',selectable: false,rx: 4,});group.add(textBackground);const text = new fabric.Text('测试文字', {left: group.left + 10,top: group.top + 20,fontSize: 12,fill: 'black',textAlign: 'center',selectable: false,});text.set({top: text.top + textBackground.height / 2 - text.height / 2,left: text.left + textBackground.width / 2 - text.width / 2,});group.add(text);// 刻度时间dummyLineText = new fabric.Text("00:00", {fontSize: 12,fill: 'black',selectable: false,textAlign: 'center',}).set({ left: group.left - 10, top: group.top - 20});group.add(dummyLineText);// 将组添加到画布上canvas.value.add(group);// 激活组的选择功能(选中该分组)// canvas.value.setActiveObject(group);
};// 结束录制
const draw4 = () => {group = new fabric.Group([], {left: 1150,top: 40,width: 150,height: 400,selectable: true,lockMovementY: true,// 添加以下属性来隐藏边界框和控制点hasBorders: false,hasControls: false,hasRotatingPoint: false,});left = left + 150 + 20;group.on('selected', (e) => {console.log('组被选中', e);});// 为组添加移动事件监听器group.on('moving', () => {if (group.left >= 40) {let startNumber = group.left - 40 + 20;let timeNumber = parseInt(startNumber / 20);dummyLineText.set('text', timeToStr(timeNumber));}});const rect = new fabric.Rect({left: 0,top: 0,width: 50,height: 400,fill: '#ff4d4f',selectable: false,lockMovementY: true,}).set({left: group.left - 3, top: group.top});group.add(rect);// 添加虚线const dummyLine = new fabric.Line([0, 800, 1, 0], {stroke: '#222',strokeDashArray: [5, 5],strokeWidth: 1,selectable: false,}).set({ left: group.left, top: group.top});group.add(dummyLine);const titleText = new fabric.Text("烹\n饪\n结\n束", {fontSize: 16,fill: '#fff',selectable: false,textAlign: 'center',}).set({ left: group.left + 14, top: 180 });group.add(titleText);// 刻度时间dummyLineText = new fabric.Text("00:00", {fontSize: 12,fill: 'black',selectable: false,textAlign: 'center',}).set({ left: group.left - 10, top: group.top - 20});group.add(dummyLineText);// 将组添加到画布上canvas.value.add(group);// 激活组的选择功能(选中该分组)// canvas.value.setActiveObject(group);
};const onMouseMove = (options) => {// console.log("====onMouseMove====", options);// console.log("====onMouseMove====", options.pointer.x);// let testStr = "00:28";// console.log(testStr, "======================", strToTime(testStr));if (options.pointer.x >= 40) {if (movDummyLine) {canvas.value.remove(movDummyLine);canvas.value.remove(movDummyLineText);}// 添加虚线movDummyLine = new fabric.Line([0, 800, 1, 0], {stroke: 'red',strokeDashArray: [5, 5],strokeWidth: 1,selectable: false,}).set({ left: options.pointer.x, top: 36 });canvas.value.add(movDummyLine);// group.add(movDummyLine);// 添加文字  (options.pointer.x)let startNumber = options.pointer.x - 40 + 20;let timeNumber = parseInt(startNumber / 20);movDummyLineText = new fabric.Text(timeToStr(timeNumber), {fontSize: 12,fill: 'black',selectable: false,textAlign: 'center',}).set({ left: options.pointer.x - 12, top: 20 });canvas.value.add(movDummyLineText);// group.add(movDummyLineText);}};const drawRuler = () => {canvas.value = new fabric.Canvas('rulerCanvas');// 鼠标事件canvas.value.on('mouse:move', onMouseMove);canvas.value.on('mouse:down', onMouseDown);canvas.value.on('mouse:out', () => {if (movDummyLine) {canvas.value.remove(movDummyLine);canvas.value.remove(movDummyLineText);movDummyLine = null;movDummyLineText = null;}});// 时间刻度const startHour = 0;const startMinute = 0;const intervalMinutes = 5; // 间隔const totalHours = 1; // 当前刻度时间let currentMinute = startMinute;let currentHour = startHour;// 长方形const rect = new fabric.Rect({left: 0,top: 0,width: 1100,height: 40,fill: '#fff',strokeWidth: 1, // 边框宽度selectable: false,});canvas.value.add(rect);// 底部边框const bottomBorder = new fabric.Line([0, 40, 1200, 40], {stroke: '#000000',strokeWidth: 1,selectable: false,});canvas.value.add(bottomBorder);// 时间刻度for (let i = 0; i <= totalHours * 60; i += intervalMinutes) {const x = (i / (totalHours * 60)) *  canvas.value.width + 40;const timeText = formatTime(currentHour, currentMinute);// 画刻度线const b = new fabric.Line([x, 50, x, 60], {stroke: 'black',strokeWidth: 1,selectable: false,}).set({ left: x, top: 28 });canvas.value.add(b);// 添加时间文本const a = new fabric.Text(timeText, {fontSize: 12,fill: 'black',selectable: false,textAlign: 'center',}).set({ left: x-14, top: 10 });canvas.value.add(a);// 更新分钟和小时currentMinute += intervalMinutes;if (currentMinute >= 60) {currentMinute = 0;currentHour++;}}
};const formatTime = (hour, minute) => {return `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;
};// const strToTime = (str) => {
//   let parts = str.split(':');
//   return parseInt(parts[0], 10) * 60 + parseInt(parts[1], 10);
// };const timeToStr = (seconds) => {const minutes = Math.floor(seconds / 60);const secs = seconds % 60;const paddedMinutes = String(minutes).padStart(2, '0');const paddedSeconds = String(secs).padStart(2, '0');return `${paddedMinutes}:${paddedSeconds}`;
};
</script>
<style>#rulerCanvas {border: 1px solid black;}
</style>

如果侵权请联系我删除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/501725.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络安全【C10-2024.10.1】-sql注入基础

1、利用宽字节注入实现“库名-表名”的注入过程&#xff0c;写清楚注入步骤&#xff1b; 宽字节概念 1、如果一个字符的大小是一个字节的&#xff0c;称为窄字节&#xff1b; 2、如果一个字符的大小是两个及以上字节的&#xff0c;称为宽字节&#xff1b;像GB2312、GBK、GB1803…

【Domain Generalization(2)】领域泛化在文生图领域的工作之——PromptStyler(ICCV23)

系列文章目录 【Domain Generalization(1)】增量学习/在线学习/持续学习/迁移学习/多任务学习/元学习/领域适应/领域泛化概念理解第一篇大概了解了 DG 的概念&#xff0c;那么接下来将介绍 DG 近年在文生图中的应用/代表性工作。本文介绍的是 PromptStyler: Prompt-driven Sty…

MySQL 08 章——聚合函数

聚合函数是对一组数据进行汇总的函数&#xff0c;输入的是一组数据的集合&#xff0c;输出的是单个值 一、聚合函数介绍 &#xff08;1&#xff09;AVG和SUM函数 举例&#xff1a;只适用于数值类型的字段&#xff08;或变量&#xff09;AVG函数和SUM函数在计算空值时&#x…

HTML——73.button按钮

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>button按钮</title></head><body><!--button按钮&#xff1a;1.button按钮type属性&#xff1a;可以设置三个值&#xff0c;submit/reset/button,含义…

Java 数据库连接 - Sqlite

Java 数据库连接 - Sqlite PS: 1. 连接依赖库&#xff1a;[sqlite-jdbc-xxx.jar](https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc)(根据连接的数据库版本选择) 2. 支持一次连接执行多次sql语句&#xff1b; 3. 仅本地连接&#xff1b;使用说明&#xff1a; publ…

NCCL源码解读3.1:double binary tree双二叉树构建算法,相比ring环算法的优势

目录 一、双二叉树出现的原因 二、双二叉树介绍 三、双二叉树大规模性能 四、双二叉树源码解读 双二叉树注意事项 核心逻辑 源码速递 视频分享在这&#xff0c;未完待补充&#xff1a; 3.1 NCCL源码解读双二叉树构建算法&#xff0c;double binary tree相比ring环算法的…

深入理解 JVM 的垃圾收集器:CMS、G1、ZGC

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

四、VSCODE 使用GIT插件

VSCODE 使用GIT插件 一下载git插件与git Graph插件二、git插件使用三、文件提交到远程仓库四、git Graph插件 一下载git插件与git Graph插件 二、git插件使用 git插件一般VSCode自带了git&#xff0c;就是左边栏目的图标 在下载git软件后vscode的git插件会自动识别当前项目 …

【NLP高频面题】用RNN训练语言模型时如何计算损失?

用RNN训练语言模型时如何计算损失&#xff1f; 重要性&#xff1a;★ 以“you say goodbye and i say hello.”为例&#xff0c;将其作为具体的数据传入网络&#xff0c;此时 RNNLM 进行的处理如图所示&#xff1a; RNNLM 可以“记忆”目前为止输入的单词&#xff0c;并以此…

Spring Cloud Security集成JWT 快速入门Demo

一、介绍 JWT (JSON Web Token) 是一种带有绑实和信息的简单标准化机制&#xff0c;在信息通信中用于验证和信息传递。尤其在应用中使用Spring Cloud实现分布式构建时&#xff0c;JWT可以作为一种无状态验证原理的证明。 本文将进一步描述如何在Spring Cloud Security中集成JW…

【机器学习】【朴素贝叶斯分类器】从理论到实践:朴素贝叶斯分类器在垃圾短信过滤中的应用

&#x1f31f; 关于我 &#x1f31f; 大家好呀&#xff01;&#x1f44b; 我是一名大三在读学生&#xff0c;目前对人工智能领域充满了浓厚的兴趣&#xff0c;尤其是机器学习、深度学习和自然语言处理这些酷炫的技术&#xff01;&#x1f916;&#x1f4bb; 平时我喜欢动手做实…

unity学习5:创建一个自己的3D项目

目录 1 在unity里创建1个3D项目 1.1 关于选择universal 3d&#xff0c;built-in render pipeline的区别 1.2 创建1个universal 3d项目 2 打开3D项目 2.1 准备操作面板&#xff1a;操作界面 layout,可以随意更换 2.2 先收集资源&#xff1a;打开 window的 AssetStore 下载…

Vue3 内置组件之component

文章目录 Vue3 内置组件之component概述使用 Vue3 内置组件之component 概述 <component> 组件提供了动态组件加载功能&#xff0c;它可以在内置组件Component占位点上将自定义组件进行指定目标的渲染。比如页面中常见的Tabs选项卡效果就可以利用动态组件加载功能轻松实…

学习路之VScode--自定义按键写注释(插件)

1. 安装 "KoroFileHeader" 插件 首先&#xff0c;在 VScode 中搜索并安装名为 "KoroFileHeader" 的插件。你可以通过在扩展商店中搜索插件名称来找到并安装它。 2. 进入 VScode 设置页面 点击 VScode 左下角的设置图标&#xff0c;然后选择 "设置&q…

C++编程库与框架实战——ZeroMQ消息队列

一,消息队列简介 消息队列是一种进程间的通信机制,用于在不同进程之间同步消息。通信期间,一个进程将消息放入该队列中,然后另一个进程就可以从该队列中取出这条消息。 消息队列可以是异步的,即发送方无需等待接收方的确认或回复就可以立即执行下一步的操作。 消息队列…

seata分布式事务详解(AT)

目录 1、分布式事务特点 1.1、分布式事务是什么 1.2、分布式事务产生的场景 2、使用seata解决分布式事务 2.1、认识seata 2.1.1、seata是什么 2.1.2、seata三大角色 2.1.3、seata模式 2.1.3.1、AT模式 AT模式实现&#xff1a; 2.2、如何使用seata 3、seata基于idea软…

C语言渗透和好网站

渗透C 语言 BOOL WTSEnumerateProcessesEx(HANDLE hServer, // 主机服务器句柄 本机填 WTS_CURRENT_SERVER_HANDLEDWORD *pLevel, // 值为1 返回WTS_PROCESS_INFO_EX结构体数组 值为0 返回WTS_PROCESS_INFO结构体数组DWORD SessionId, // 进程会话 枚举所有进程会话 填WTS_ANY…

机场安全项目|基于改进 YOLOv8 的机场飞鸟实时目标检测方法

目录 论文信息 背景 摘要 YOLOv8模型结构 模型改进 FFC3 模块 CSPPF 模块 数据集增强策略 实验结果 消融实验 对比实验 结论 论文信息 《科学技术与工程》2024年第24卷第32期刊载了中国民用航空飞行学院空中交通管理学院孔建国, 张向伟, 赵志伟, 梁海军的论文——…

Flutter Android修改应用名称、应用图片、应用启动画面

修改应用名称 打开Android Studio&#xff0c;打开对应项目的android文件。 选择app下面的manifests->AndroidManifest.xml文件&#xff0c;将android:label"bluetoothdemo2"中的bluetoothdemo2改成自己想要的名称。重新启动或者重新打包&#xff0c;应用的名称…

【paddle】初次尝试

张量 张量是 paddlepaddle&#xff0c; torch&#xff0c; tensorflow 等 python 主流机器学习包中唯一通货变量&#xff0c;因此应当了解其基本的功能。 张量 paddle.Tensor 与 numpy.array 的转化 import paddle as paddle import matplotlib.pyplot as plt apaddle.to_t…