图形编辑器开发:自定义光标管理

news2024/11/17 3:41:35

大家好,我是前端西瓜哥。

今天来讲讲如何在图形编辑器中使用自定义光标,并对光标其进行管理。

编辑器 github 地址:

https://github.com/F-star/suika

线上体验:

https://blog.fstars.wang/app/suika/

自定义光标的意义是什么?

光标(游标)在图形界面交互中是非常基础的一环。

它是一个指针,悬浮在屏幕的最上层。除了可以标记出指针的当前位置,同时也会通过它独特的样式,提示用户此时可以执行怎么的操作

比如抓手(grab)光标,是一个展开的手掌,表示可以对目标进行拖拽操作。

缩放(xx-resize)光标,是一个有方向的单(双)箭头,表示可以往特定方向移动以改变目标大小。

长得像英文字母 I 的文字(text)光标,则提示可以进行文字的操作,细瘦的垂直线是为了更好地点中字符之间的空白区域。

点击(pointer)光标,一根手指(食指,不是中指)伸出来是要干嘛,是为了试探,看到按钮就尝试点一下,表示某个区域是可点击的。

在这里插入图片描述

操作系统有丰富的光标样式可以选择,在 Web 网页中可以通过 cursor 样式属性进行设置

对于一般应用来说,通常是够用的。但对于一个成熟的图形编辑器来说,这还远远不够。

我们还需要一些 更具体的光标样式来向用户传递信息,比如:

  1. 旋转光标:表示图形可旋转。cursor 属性中没有旋转光标,勉强可用抓手工具做个平替;
  2. 支持任意度数的缩放光标。cursor 属性的缩放光标只有 45 度的正数倍数光标,这精度远远不够。
  3. 钢笔工具相关光标:钢笔光标、锚点光标、新增/删除点光标;

等等。

在这里插入图片描述

此外,自定义光标还有一个很重要的作用,就是 实现不同平台的视觉一致性

不同操作系统的 UI 风格是不同的,它们的光标是相当不一致的,会给用户带来不同的体验。

(我希望在 Windows 系统看到 MacOS 的光标)

如何支持自定义光标

没有光标,我们自己造。

好在 cursor 是支持自定义光标的。

具体用法如下。

.suika-cursor-default {
  cursor: url(./cursor-icons/suika-cursor-default.png) 5 5, pointer;
}

值依次为:

  1. url(<url>):自定义光标的图片资源 url,因为不大且不希望额外作为单独资源加载,通常会选择转换为 base64 格式内嵌;
  2. x y:使用相对图片左上角的像素位置作为光标位置;
  3. <keyword>:如果没有指定自定义光标图片,或者加载光标资源失败,就会使用浏览器支持的光标值,比如 pointer。

我们需要绘制好光标图片,然后导出为 png(背景为透明度),然后定义好 x 和 y,再通过 css 类包裹一下,然后根据需要在 Canvas 上设置对应的 css 样式即可。

在这里插入图片描述

多种旋转角度的旋转和缩放光标

有两种光标比较特殊,它们有特殊的旋转角度的参数。

它们就是旋转和缩放光标。

在这里插入图片描述

因为 cursor 这个 css 属性并不支持设置旋转角度,所以我们只能绘制 0 到 359 之间度数共 360 个不同的旋转光标图片

缩放光标因为其样式中心对称的原因,倒是不需要这么多,只要绘制 0 到 179 共 180 个图片。

然后是 精细度的问题

你这里可以整一些猫腻,比如偷懒,抽走一些度数,只给偶数的度数,比如 2、4,奇数的度数都丢掉,没有 1、3 这些度数。设置光标的时候舍入一下,找最接近的度数。

或者你精益求精,你说间隔 1 度未免太大,我们要更精确一点,我们不仅支持整数,我们还要支持 1.5、6.5 这种中间值,我们要用 720 个图片。

没问题,都可以上。

批量生成方案

但是呢,我们发现,这些光标其实都来自一个源图片,只是旋转了不同的角度,我们手工一个个操作未免太低效了。

这时候我们就可以自己写或找一些工具,批量对一张源图形生成旋转多种角度后的图片,然后再写个脚本去自动生成 css 代码,把这些图片引入进去

这是一个方案,figma 是这么做的。

感觉还是有亿点麻烦。没事,我们有另一个方案。

上面做的是打包前生成大量图片,那我们可不可以在运行时动态生成光标呢?

可以的。图片有位图的,也有矢量的啊,我们可以用一种叫做 SVG 的特殊图片格式,它的内容是文本,一种的 xml 文本。

我们可以将光标 UI 导出为 SVG,然后在最顶层的元素加上 transform 的旋转变换。

可以写一个方法,传入角度和位置信息,动态生成对应的 SVG 字符串,然后转成 DataURL 给 cursor 应用上

大概像这样:

const getRotationIconDataUrl = (degree, x = 0, y = 0) => {
  return `url("data:image/svg+xml,
  	...
  	<g fill='none' transform='rotate(${degree} ${x} ${y})'
  	...
  ") ${x} ${y}, pointer`
}

canvas.style.cursor = getRotationIconDataUrl(114.544);

开源白板工具 tldraw 选择了这个方案。

在这里插入图片描述

你可以给一个精度很高的旋转度数。

在这里插入图片描述

模块设计

代码设计上,我们会设计一个 CursorManager 类进行光标的管理。

这个类最重要的作用就是设置光标值。

setCursor 方法接收一个光标值,除了支持传统的字符串,也支持 { type: 'rotation'; degree: 45 } 这种形式。

它主要做了如下操作:

  1. 标准化光标值,比如把度数取余到 0~360 内;
  2. 比较新旧光标值,相同就跳过;
  3. 清空原来设置的光标样式;
  4. 根据光标不同,执行各自的逻辑。

下面是核心代码实现。

// 支持的光标类型
export type ICursor =
  | 'default'
  | { type: 'resize'; degree: number }
  | { type: 'rotation'; degree: number }
  | 'grab'
	| ...
}

class CursorManager {
  private cursor: ICursor;
  

  setCursor(cursor: ICursor) {
    // 1. 标准化光标值,比如把度数取余到 0~360 内
    cursor = this.normalizeCursor(cursor);
    // 2. 比较新旧光标值,相同就跳过
    if (isEqual(cursor, this.cursor)) {
      return;
    }
    this.cursor = cursor;
    
    const clsPrefix = 'suika-cursor-';
    // 3. 清空原来设置的光标样式
    canvasElement.classList.forEach((className) => {
      if (className.startsWith(clsPrefix) {
        canvasElement.classList.remove(className);
      }
    });
    this.editor.canvasElement.style.cursor = '';
    
    // 4. 根据光标不同,执行各自的逻辑
    if (this.customClassCursor.has(cursor)) {
      // 这个是注册了 class 的光标
      const className = `${clsPrefix}${cursor}`;
      this.editor.canvasElement.classList.add(className);
    } else if (typeof cursor == 'string') {
      // 用浏览器自带的
      this.editor.canvasElement.style.cursor = cursor;
    } else if (cursor.type === 'resize') {
      // 后面都是使用动态 svg 字符串
      this.setResizeCursorInCanvas(cursor.degree);
    } else if (cursor.type === 'rotation') {
      this.setRotationCursorInCanvas(cursor.degree);
    }
  }
}

绘制在画布上的光标

光标还有一种比较少用的方案,也说说吧。

就是有些光标是绘制在画布上的。

一个经典的例子就是 AutoCAD 的十字光标,这个十字的长度是可以设置的,可以相当长。

在这里插入图片描述

如果你修改操作系统的光标,那这个十字便会突破天际地显示到非绘制区域上。

此外,AutoCAD 的光标并不忠实跟随操作系统光标,比如有时候会吸附于某点不动,并基于它的位置显示下拉菜单,此时可以用真正的光标去点选。

在这里插入图片描述

考虑到性能,建议把光标放到另一个 canvas 上,和图形放一个 canvas 会让画布中没做任何操作的图形频繁重绘。

结尾

总结一下。

关于图形编辑器的光标,我们有以下方案:

  1. 使用浏览器本身就提供的一些光标值。优点是成本低,缺点是样式有限,且不同操作系统风格差异大;
  2. cursor 支持自定义光标,所以我们可以自己设置自己的一套光标去应用。但其中有一些比较特殊的有各种旋转方向的光标,需要做特别的处理。一种是用工具批量生产光标图片,一种是利用 svg 在运行时动态生成;
  3. 最后是在画布上渲染光标的方案,适合一些有特殊需求的图形编辑器。这类图形编辑器的光标往往可以自定义,且可以非常大,或是它们在某些场景下会脱离鼠标的控制,喜欢特立独行,比如突然吸附到某个吸附点上。缺点是实现比较复杂,你可能需要像管理图形一样去管理它。

我是前端西瓜哥,欢迎关注我,学习更多图形编辑器知识。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript的学习之BOM和DOM,就这一篇就OK了!(超详细)

目录 Day28 JavaScript(2) 1、BOM对象 1.1 window对象 1.2 Location ( 地址栏)对象 1.3 本地存储对象 2、DOM对象(JS核心) 2.1 查找标签 2.2 绑定事件 2.3 操作标签 2.4 常用事件 Day28 JavaScript(2) 1、BOM对象 BOM:Broswer object model,即浏览器提供我们开发者…

ssh远程连接不了虚拟机ubuntu

直奔主题 1. 确保linux安装了ssh2.查看网络适配器是否启用3.连接成功 1. 确保linux安装了ssh sudo apt-get install openssh-server2.查看网络适配器是否启用 3.连接成功

“AI在未来”公益计划,亚马逊云科技将教育资源带到更多中西部学校

亚马逊云科技宣布携手中国光华科技基金会启动“AI在未来”公益计划2023至2024学年项目&#xff0c;预计本学年内在内蒙古、江西、湖南和广西四个省份开展该项目&#xff0c;并完成三年内为中西部地区一百所学校的一万名学生提供免费人工智能教育资源及实践机会的目标。 此外&am…

基于像素特征的kmeas聚类的图像分割方案

kmeans聚类代码 将像素进行聚类&#xff0c;得到每个像素的聚类标签&#xff0c;默认聚类簇数为3 def seg_kmeans(img,clusters3):img_flatimg.reshape((-1,3))# print(img_flat.shape)img_flatnp.float32(img_flat)criteria(cv.TERM_CRITERIA_MAX_ITERcv.TERM_CRITERIA_EPS,2…

《白帽子讲web安全》

第十四章 PHP安全 文件包含漏洞是“代码注入”的一种。“代码注入”这种攻击&#xff0c;其原理就是注入一段用户能控制的脚本或代码&#xff0c;并让服务器端执行。“代码注入”的典型代表就是文件包含&#xff08;File Inclusion&#xff09;。文件包含可能会出现在JSP、PHP…

如何有效的禁止Google Chrome自动更新?

禁止Chrome自动更新 1、背景2、操作步骤 1、背景 众所周知&#xff0c;当我们在使用Selenium进行Web自动化操作&#xff08;如爬虫&#xff09;时&#xff0c;一般会用到ChromeDriver。然而Driver的更新速度明显跟不上Chrome的自动更新。导致我们在使用Selenium进行一些操作时就…

新加坡服务器托管-金融企业的选择

新加坡作为一个亚洲金融中心&#xff0c;其优越的地理位置和先进的信息通信技术基础设施&#xff0c;使得其成为了众多金融机构企业选择服务器机房托管的理想地点。金融行业对于服务器的安全性和可靠性要求很高&#xff0c;而将服务器托管在新加坡有许多好处。 首先&#xff0c…

新零售数字化系统提供商怎么选择?2023十大收银系统排行榜-亿发

随着零售业务的日益繁荣和电子商务的迅猛发展&#xff0c;零售收银系统已成为各类商家提高效率、管理库存、提供更好服务的不可或缺的工具。然而&#xff0c;在众多的收银系统中&#xff0c;如何选择一款适合自己的&#xff0c;一直是许多商家头疼的问题。今天我们就来盘点一下…

java项目之木里风景文化管理平台(ssm+vue)

项目简介 木里风景文化管理平台实现了以下功能&#xff1a; 前台功能&#xff1a;用户进入系统可以实现首页&#xff0c;旅游公告&#xff0c;景区&#xff0c;景区商品&#xff0c;景区美食&#xff0c;旅游交通工具&#xff0c;红黑榜&#xff0c;个人中心&#xff0c;后台…

Spark---核心介绍

一、Spark核心 1、RDD 1&#xff09;、概念&#xff1a; RDD&#xff08;Resilient Distributed Datest&#xff09;&#xff0c;弹性分布式数据集。 2&#xff09;、RDD的五大特性&#xff1a; 1、RDD是由一系列的partition组成的 2、函数是作用在每一个partition(split…

SSM高考志愿辅助推荐系统-计算机毕业设计附源码21279

目 录 摘要 1 绪论 1.1 研究背景 1.2研究意义 1.3论文结构与章节安排 2 高考志愿辅助推荐系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析 2…

基于社交网络算法优化概率神经网络PNN的分类预测 - 附代码

基于社交网络算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于社交网络算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于社交网络优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

wsl安装ubuntu的问题点、处理及连接

WSL安装Ubuntu的参考链接 (41条消息) wsl报错&#xff1a;WslRegisterDistribution failed with error: 0x800701bc_yzpyzp的博客-CSDN博客_0x800701bc wsl (41条消息) 使用Ubuntu安装软件出现Unable to locate package错误解决办法_大灰狼学编程的博客-CSDN博客 手把手教你…

excel自定义函数之汉字转为拼音及大写字母

使用场景&#xff1a;想把姓名转化为拼音格式&#xff0c;然后拼音转为大写字母 至于怎么在excel里面自定义函数&#xff0c;自行百度都有&#xff0c;这里简单截图看看。 步骤&#xff1a;文件》选项》自定义功能区》 打开编辑窗口 把下面这段代码粘贴就能实现汉字转化为拼音…

医疗器械维修售后技术培训与支持的重要性

医疗器械维修售后技术培训与支持的重要性 随着我国医疗器械产业的的高速发展、医疗器械企业的崛起,大量创新医疗器械产品进入医疗机构。但医疗设备在使用和维护过程中,暴露出许多问题和不足,如部分设备故障率较高、临床工程培训不足、售后服务模式整体比较落后等,这影响了医疗…

【JUC】十、ForkJoin

文章目录 1、分支合并框架2、案例3、ForkJoinTask4、工作窃取算法5、ForkJoinPool 一个个任务执行在一个个线程上&#xff0c;倘若某一个任务耗时很久&#xff0c;期间其他线程都无事可做&#xff0c;显然没有利用好多核CPU这一计算机资源&#xff0c;因此&#xff0c;出现了&q…

红队攻防之Goby反杀

若结局非我所愿&#xff0c;那就在尘埃落定前奋力一搏。 本文首发于先知社区&#xff0c;原创作者即是本人 一、弹xss 为了方便&#xff0c;本次直接使用 PhpStudy 进行建站&#xff0c;开启的web服务要为MySQLNginx&#xff0c;这里的 PhpStudy 地址为 http://x.x.x.x&…

AC修炼计划(AtCoder Beginner Contest 329)

传送门&#xff1a;&#xff33;&#xff4b;&#xff59; Inc, Programming Contest 2023&#xff08;AtCoder Beginner Contest 329&#xff09; - AtCoder A&#xff0c;B&#xff0c;C&#xff0c;D 这四道题比较简单&#xff0c;就不多叙述。 E - Stamp 这题是一道比较…

模拟量采集----测量输入的电压

生活中的模拟量有很多 大多都为电压信号和电流信号 今天讲如何测量输入的电压信号 由图中的黄线可知&#xff0c;该运放是采用的同相放大器中的电压跟随器 电压跟随器的特点是电压的输入和输出隔离 同相运放的输入与输出的关系是&#xff1a;输出1R2/R1 在图上对应的就是输…

Spring:IoC,AOP的简单理解与使用

IoC容器 IoC &#xff0c;Spring全家桶各个功能模块的基础&#xff0c;是创建对象的容器。 IoC概念 控制反转&#xff0c;将对象的创建进行反转&#xff0c;常规情况下对象由开发者手动创建&#xff0c;而使用IoC不再需要创建对象&#xff0c;由IoC容器根据需求自动创建项目…