图片标注编辑平台搭建系列教程(4)——fabric几何定制渲染

背景

标注的几何,有时需要一些定制化的渲染样式,例如,线中间展示箭头,表示方向。本期教程教大家如何实现fabric几何定制化渲染。

带箭头的线

fabric提供了一些原生的几何,例如Point、Polyline、Polygon。同时提供了一些抽象的实体,如Object、Path。

如果使用原生的几何,可配置的样式是有限的。

比如Point的配置是:

{radius: 5,stroke: 'rgba(0,0,0,1)',strokeWidth: 2,fill: 'rgba(255,0,0,1)'
}

Polyline的配置是:

{stroke: 'rgba(0,0,0,1)',strokeWidth: 5
}

Polygon的配置是:

{fill: 'rgba(255,0,0,0,3)',strokeWidth: 1
}

可见这些线宽、线颜色、填充色等并不能实现箭头样式,需要对这些类进行扩展。

fabric提供了方法,可以扩展原生几何,代码如下:

export const NewPolyline = fabric.util.createClass(fabric.Polyline, {type: 'NewPolyline',initialize: function (points: any = [], options: any = {}) {this.callSuper('initialize', points, { ...options }); // 调用Polyline的初始化方法},_render: function (ctx: any) {// 自定义渲染,每次canvas.renderAll()都会触发该方法。this.callSuper('_render', ctx);}
}

此时我们得到了一个新的fabric几何类型:NewPolyline。其初始化参数和Polyline一致,points是点序,options里设置其样式。

而箭头样式,需要在_render方法里实现。_render方法可以拿到ctx,即canvas的实例,我们可以利用ctx进行各种绘制操作。

注意:在_render这个步骤,每个几何都有自己的坐标系,其坐标系原点是几何的外接矩形的中心点。

因此,我们每个坐标都需要减去当前几何的width/2和height/2,进行原点平移。

举个例子,比如我们有一条线,其参数如下:

{left: 10,top: 10,points: [{x: 0, y: 0}, {x: 5, y: 0}, {x: 5, y: 5}],
}

在坐标系中,如图,

left和top将要素的坐标系从O移动到了O',在此基础上,绘制折线[[0,0],[5,0],[5,5]]。

在渲染时,fabric又将坐标原点O'平移到外接矩形的中心点O''。

知道坐标系后,我们先来求线段的中点:

const points = this.get('points');
const width = this.get('width');
const height = this.get('height');for (let i = 0; i < points.length; i++) {const midX = (points[i].x + points[i + 1].x) / 2 - width / 2;const midY = (points[i].y + points[i + 1].y) / 2 - height / 2;console.log(midX, midY);
}// 结果:
// -2.5, -2.5
// 2.5, -2.5
// 2.5, 2.5

看懂上面的代码,你就可以以线段中心点为中心,画沿着线段的三角形了,代码如下:

for (let i = 0; i < points.length - 1; i++) {const midX = (points[i].x + points[i + 1].x) / 2 - width / 2;const midY = (points[i].y + points[i + 1].y) / 2 - height / 2;const rotate = Math.atan2(points[i + 1].y - points[i].y, points[i + 1].x - points[i].x);ctx.moveTo(midX, midY);const firstX = midX - (arrowWidth / 2) * Math.sin(rotate);const firstY = midY + (arrowWidth / 2) * Math.cos(rotate);ctx.lineTo(firstX, firstY);const secondX = midX + (arrowWidth / 2) * Math.sqrt(3) * Math.cos(rotate);const secondY = midY + (arrowWidth / 2) * Math.sqrt(3) * Math.sin(rotate);ctx.lineTo(secondX, secondY);const thirdX = midX + (arrowWidth / 2) * Math.sin(rotate);const thirdY = midY - (arrowWidth / 2) * Math.cos(rotate);ctx.lineTo(thirdX, thirdY);ctx.closePath();ctx.fill();}

效果图如下:

了解这个原理,你就可以利用canvas的绘制操作实现任何的自定义样式。

缩放控制线宽等宽

上一章我们讲到了,画布是可以拖拽和缩放的,本质上是修改canvas的transform。

在每次缩放后,canvas会调用renderAll方法,从而调用每个几何的_render方法。在_render内,我们需要重新计算strokeWidth:

const strokeWidth = 5;
const zoom = canvas.getZoom();
this.set({strokeWidth: strokeWidth / zoom
});

这样可以保证每次缩放后,线宽依然维持一个固定值。如果我们不修改线宽,则会被同样得缩放。

预告

下一章,我们详细聊一个极其隐蔽的问题:线居中渲染。

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

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

相关文章

LangChain入门:2.OpenAPI调用ChatGPT模型

引言 在本文中&#xff0c;我们将带您深入探索如何通过OpenAPI与ChatGPT模型进行高效交互&#xff0c;实现智能文本问答功能。通过LangChain库的实践&#xff0c;您将学习构建一个能够与用户进行自然语言对话的系统的关键步骤。 准备步骤 在动手编码之前&#xff0c;请确保您…

Collection与数据结构链表与LinkedList(三):链表精选OJ例题(下)

1. 分割链表 OJ链接 class Solution {public ListNode partition(ListNode head, int x) {if(head null){return null;//空链表的情况}ListNode cur head;ListNode formerhead null;ListNode formerend null;ListNode latterhead null;ListNode latterend null;//定义…

Beans模块之工厂模块DisposableBean

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

宝塔面板操作一个服务器域名部署多个网站

此处记录IP一样&#xff0c;端口不一样的操作方式&#xff1a; 宝塔面板操作&#xff1a; 1、创建第一个网站&#xff1a; 网站名用IP地址&#xff0c;默认80端口。 创建好后&#xff0c;直接IP访问就可以了。看到自带的默认首页 2、接下来部署第二个网站&#xff1a; 仍然是…

docker-compose mysql

使用docker-compose 部署 MySQL&#xff08;所有版本通用&#xff09; 一、拉取MySQL镜像 我这里使用的是MySQL8.0.18&#xff0c;可以自行选择需要的版本。 docker pull mysql:8.0.18二、创建挂载目录 mkdir -p /data/mysql8/log mkdir -p /data/mysql8/data mkdir -p /dat…

IC-随便记

1、移远通信---通信模组 物联网解决方案供应商&#xff0c;可提供完备的IoT产品和服务&#xff0c;涵盖蜂窝模组(5G/4G/3G/2G/LPWA)、车载前装模组、智能模组&#xff08;5G/4G/边缘计算&#xff09;、短距离通信模组(Wi-Fi&BT)、GNSS定位模组、卫星通信模组、天线等硬件产…

阿里云CentOS7安装MySQL8

创建目录 [rootnode1 ~]# mkdir /usr/local/mysql [rootnode1 ~]# cd /usr/local/mysql/ 下载安装包 到MySQL官网查看需要下载的版本&#xff0c;并获取到下载地址 https://downloads.mysql.com/archives/community/下载 [rootnode1 mysql]# wget https://downloads.mysql…

Linux - 第三节

改变用户类型 su 仅单纯的进行身份变化 依旧处于普通用户里面 su - 进行重新登录更改身份 退出用exit / ctrld su 用户名 改成成其他身份 对一条命令进行提权 sudo command r:可读 w:可写 x:可执行 -:对应的权限位置&#xff0c;没有权限 去掉所有权限 chmod u…

java日志技术——Logback日志框架安装及概述

前言&#xff1a; 整理下学习笔记&#xff0c;打好基础&#xff0c;daydayup!!! 日志 什么是日志 程序中的日志&#xff0c;通常就是一个文件&#xff0c;里面记录的是程序运行过程中的各种信息&#xff0c;通过日志可以进行操作分析&#xff0c;bug定位等 记录日志的方案 程…

嵌入式系统基础知识(一):嵌入式系统是什么?

一.定义 根据IEEE&#xff08;国际电气和电子工程师协会&#xff09;的定义&#xff0c;嵌入式系统是“控制、监视或者辅助设备、机器和车间运行的装置”。这主要是从应用上加以定义的&#xff0c;从中可看出嵌入式系统是软件和硬件的综合体&#xff0c;还可以涵盖机械等附属装…

云渲染实用工具:3ds max怎么改低版本?

3ds Max是建模领域广泛采用的专业软件&#xff0c;它通过定期更新来不断增强功能和提升性能。但这些频繁的更新有时会导致一些插件暂时无法与新版本完全兼容。为了解决这个问题&#xff0c;设计师们可以采用一个简单有效的方法&#xff0c;那就是将较新版本的3ds Max文件进行版…

Java常见限流用法介绍和实现

目录 一、现象 二、工具 ​​​​​​1、AtomicInteger,AtomicLong 原子类操作 ​​​​​​2、RedisLua ​​​​​​3、Google Guava的RateLimiter 1&#xff09; 使用 2&#xff09; Demo 3&#xff09; 优化demo 4、阿里开源的Sentinel 三、算法 1、计数限流 &…

python练习二

# Demo85def pai_xu(ls_test):#创建一个列表排序函数命名为pai_xu# 对创建的函数进行注释"""这是一个关于列表正序/倒序排列的函数:param ls_test: 需要排序的列表:return:"""ls1 [int(ls_test[i]) for i in range(len(ls_test))]#对input输入的…

基于Java+Springboot+vue高校资源共享交流平台设计和实现

博主介绍&#xff1a;✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

C++刷题篇——05静态扫描

一、题目 二、解题思路 注意&#xff1a;注意理解题目&#xff0c;缓存的前提是先扫描一次 1、使用两个map&#xff0c;两个map的key相同&#xff0c;map1&#xff1a;key为文件标识&#xff0c;value为文件出现的次数&#xff1b;map2&#xff1a;key为文件标识&#xff0c;va…

手写简易操作系统(十九)--实现0x80中断

前情提要 上一节我们实现了用户程序&#xff0c;现在的用户程序还是一个函数来模拟的&#xff0c;后面我们会把编译好的用户程序放在硬盘里&#xff0c;通过硬盘加载。 众所周知&#xff0c;用户程序使用系统服务是通过 0x80 中断进行的&#xff0c;也只能通过中断进入高优先…

CTF wed安全(攻防世界)练习题

一、Training-WWW-Robots 进入网站如图&#xff1a; 翻译&#xff1a;在这个小小的挑战训练中&#xff0c;你将学习Robots exclusion standard。网络爬虫使用robots.txt文件来检查它们是否被允许抓取和索引您的网站或只是其中的一部分。 有时这些文件会暴露目录结构&#xff0c…

Qt6.6添加多媒体模块Multimedia报错问题

问题 QT包含多媒体模块Multimedia时提示未知的模块&#xff1a; error: Project ERROR: Unknown module(s) in QT: multimedia 在帮助文档中只可以找到QMediaPlayer类&#xff0c;但是点进去是空的&#xff0c;这是因为没有安装多媒体模块及对应的帮助文档。 解决 使用在线…

StringBuffer与StringBuilder

1.区别 (1). String : 不可变字符序列. (2). StringBuffer : 可变字符序列.线程安全&#xff0c;但效率低. (3). StringBuilder : 可变字符序列.线程不安全&#xff0c;但效率高. 既然StringBuffer与StringBuilder都是可变字符序列&#xff0c;但二者咋区分开呢&#xff1f…

黑马点评项目笔记 II

基于Stream的消息队列 stream是一种数据类型&#xff0c;可以实现一个功能非常完善的消息队列 key&#xff1a;队列名称 nomkstream&#xff1a;如果队列不存在是否自动创建&#xff0c;默认创建 maxlen/minid&#xff1a;设置消息队列的最大消息数量 *|ID 唯一id&#xff1a;…