图片标注编辑平台搭建系列教程(3)——画布拖拽、缩放实现

简介

标注平台很关键的一点,对于整个图片为底图的画布,需要支持缩放、拖拽,并且无论画布位置在哪里,大小如何,所有绘制的点、线、面的坐标都是相对于图片左上角的,并且,拖拽、缩放,点、线、面的坐标不改变。

要实现这一点,其实就是理解这个画布的坐标系,以及变换矩阵。

画布

fabric.js的底层是canvas绘图。canvas作为画布,要支持拖拽、缩放,就需要有个容器监听鼠标事件。如果我们直接对canvas dom进行拖拽、缩放,有两个缺陷:1是后续只能在最新的canvas范围内进行缩放、拖拽,不易点击;2是dom大小和位置变更,会不断触发浏览器的重绘,性能开销较大,容易卡顿。

为了解决上述问题,我提供以下解决方案:

以一个固定位置的canvas作为画布,拖拽、缩放,只改变canvas的变形矩阵即可transform(a, b, c, d, e, f)。

a:水平方向的缩放;b:竖直方向的倾斜偏移;c:水平方向的倾斜偏移;d:竖直方向的缩放;e:水平方向的移动;f:竖直方向的移动

因此,我们的平移可以通过设置e和f,缩放可以通过设置a和d。

在fabric.js中,可以通过viewportTransform属性,获取这六个参数。然后再通过setViewportTransform方法,设置这六个参数。

居中

画布居中是标注平台必须的功能,因为你拖拽缩放后图像可能偏离视图范围很多,需要快速复原。居中的功能,说起来简单,但是实现还是有一些困难。其原理如下图所示。

首先,获取要标注的图片,宽100,高80,设置canvas的背景图片。

const canvas = new fabric.Canvas(domId, {width: 100,height: 80
}); 
canvas.setBackgroundImage('图片链接');

其次,初始化canvas的为父容器的宽高(100x60),初始化变换矩阵[1,0,0,1,0,0],即平移0,缩放1(不缩放)。

canvas.setDimensions({ width: 100, height: 60 });
canvas.setViewportTransform([1,0,0,1,0,0]);

再次,根据canvas父容器的宽高(100x60),按图像能全展示的比例对canvas视图范围进行等比缩放(0.75),得到新的宽高75x60。即a=0.75,d=0.75。

canvas.setCanvasZoom(0.75);

最后,将canvas视图范围往右平移15,即e=15,f=0。

canvas.setViewportTransform[0.75,0,0,0.75,15,0]

这样,我们的canvas始终是父容器的范围,而viewport范围已经居中。

注意,变换矩阵设置后,还需要执行渲染重置方法,这个是fabric的底层逻辑漏洞,如果不重置,画布上的点线面的位置会偏移。

canvas.renderAndReset();

平移

首先,我们需要监听canvas的mousedown、mousemove、mouseup。

mousedown的回调函数,我们设置全局变量active为true(用于mousemove判断是否需要执行平移),并记录lastX = e.pageX和lastY = e.pageY。

mousemove的回调函数,如果active为true,我们根据e.pageX - lastX和e.pageY - lastY确定横向和纵向的平移量,再通过对transform的e和f进行叠加这个平移量,实现平移功能。

mouseup的回调函数,设置acitve为false。

缩放

监听canvas的wheel,鼠标滚轮事件。

wheel的回调函数,如果e.deltaY > 0,则表示缩小,否则表示放大。缩小,我们对当前的缩放比例统一进行 -0.1,放大统一 +0.1。如果你想更人性化,可以对缩放比例做非线性的变化。

注意点

canvas的缩放,是基于canvas左上角的原点,并非基于平移后的点为原点。因此,需要先将canvas的平移参数置为0后,再设置缩放比例。

另外,我们的缩放,是基于鼠标当前位置为原点进行缩放。如图,红色点处(x, y)进行缩放,视图从v1变到v2,我们已知两个视图对应的缩放比例z1和z2,要求deltaX和deltaY,根据相似定理得:

deltaX = (x - left) * (1 - z2 / z1)

deltaY = (y - top) * (1 - z2 / z1)

newLeft = deltaX + left

newTop = deltaY + top

因此,我们在上一步设置好缩放比例后,执行平移操作(e=newLeft,f=newTop),从而将视图从黑色框缩放至蓝色框。

预告

下一章, 聊聊fabric的几何定制化渲染。

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

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

相关文章

linux之进程

一、背景 冯.诺依曼体系结构 输入设备键盘、鼠标、摄像头、话筒、磁盘、网卡...输出设备显示器、声卡、磁盘、网卡...CPU运算器、控制器存储器一般就是内存 数据在计算机的体系结构进行流动,流动过程中,进行数据的加工处理,从一个设备到另一…

Qt 多线程QThread的四种形式

重点: 1.互斥量:QMutex配套使用,lock(),unlock(),如果一个线程准备读取另一个线程数据时候采用tryLock()去锁定互斥量,保证数据完整性。 QMutexLocker简化版的QMutex,在范围区域内使用。 QMutex mutex QMutexLocker locker(&…

【unity】如何汉化unity编译器

在【unity】如何汉化unity Hub这篇文章中,我们已经完成了unity Hub的汉化,现在让我们对unity Hub安装的编译器也进行下汉化处理。 第一步:在unity Hub软件左侧栏目中点击安装,选择需要汉化的编译器,再点击设置图片按钮…

Dubbo启动流程

Java面试题 Dubbo启动流程 1.服务提供者将服务实例化后注册到注册中心。 2.服务消费者向注册中心订阅所需的服务。 3.注册中心将服务提供者注册的服务地址推送给服务消费者,同时基于长链接推送变更。 4.服务消费者通过代理对象(Proxy)发起远…

Java毕业设计-基于springboot开发的休闲娱乐代理售票系统-毕业论文+答辩PPT(附源代码+演示视频)

文章目录 前言一、毕设成果演示(源代码在文末)二、毕设摘要展示1、开发说明2、需求分析3、系统功能结构 三、系统实现展示1、系统功能模块2、后台登录2.1管理员功能2.2用户功能 四、毕设内容和源代码获取总结 Java毕业设计-基于springboot开发的休闲娱乐…

腾讯云优惠券领取及使用常见问题解答

随着云计算的普及,腾讯云作为国内领先的云计算服务提供商,为越来越多的企业和个人提供了丰富的云产品和服务。为了帮助用户更好地了解和使用腾讯云优惠券,本文将为大家解答关于腾讯云优惠券领取及使用的常见问题。 一、腾讯云优惠券概述 腾讯…

利用互斥锁解决缓存击穿问题

2.9 利用互斥锁解决缓存击穿问题 核心思路:相较于原来从缓存中查询不到数据后直接查询数据库而言,现在的方案是 进行查询之后,如果从缓存没有查询到数据,则进行互斥锁的获取,获取互斥锁后,判断是否获得到了…

包子凑数(蓝桥杯,闫氏DP分析法)

题目描述: 小明几乎每天早晨都会在一家包子铺吃早餐。 他发现这家包子铺有 N 种蒸笼,其中第 i 种蒸笼恰好能放 Ai 个包子。 每种蒸笼都有非常多笼,可以认为是无限笼。 每当有顾客想买 X 个包子,卖包子的大叔就会迅速选出若干笼…

爱上数据结构:顺序表和链表

一、线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构,也就说是连续的一条…

netty rpc框架 即时通讯

Netty是Java领域有名的开源网络库,特点是高性能和高扩展性,因此很多流行的框架都是基于它来构建的,比如我们熟知的Dubbo、Rocketmq、Hadoop等,针对高性能RPC,一般都是基于Netty来构建,比如sock-bolt。总之一…

2024-3-28 市场情绪强修复

这一轮退潮负反馈都修复了, 艾艾精工 博信股份 安奈尔 永悦科技 大理药业 ,高新发展 也补跌了,收尸队也干活了,情绪不修复不接力得最好写照。这轮周期 宁科生物 已经7板,已经追平了 博信股份7板,看明天溢…

18.字面量

文章目录 一、字面量二、区分技巧三、扩展: /t 制表符 一、字面量 在有些资料,会把字面量说成常量、字面值常量,这种叫法都不是很正确,最正确的叫法还是叫做:字面量。 作用:告诉程序员,数据在…

环信IM集成教程---消息转发合并转发的实现

前言 在发送消息体系中,转发消息是一个重要的环节,可以单条转发也可以合并转发。本文教大家在接入环信IM过程中如何实现单条转发,合并转发消息功能,同时举例一些容易踩坑的位置,以便大家尽快顺利的实现转发消息功能。…

高效 CUDA 调试:将 NVIDIA Compute Sanitizer 与 NVIDIA 工具扩展结合使用并创建自定义工具

高效 CUDA 调试:将 NVIDIA Compute Sanitizer 与 NVIDIA 工具扩展结合使用并创建自定义工具 NVIDIA Compute Sanitizer 是一款功能强大的工具,可以节省您的时间和精力,同时提高 CUDA 应用程序的可靠性和性能。 在 CUDA 环境中调试代码既具有挑…

Exception in thread “main“ com.fasterxml.jackson.databind.JsonMappingException:

问题:jaskson反序列化超出最大长度 Caused by: com.fasterxml.jackson.core.exc.StreamConstraintsException: String length (5043456) exceeds the maximum length (5000000) 场景:前端传递过大base64 原因: jaskon默认已经限制了最大长…

20个超实用Python魔法方法

大家好!今天我们要一起探索Python世界的神秘角落——那些被称为“魔法方法”的特殊成员方法。它们就像是编程中的魔法咒语,赋予你的类各种神奇特性,让你的代码更加简洁、强大且有趣味! __init__:这是每个对象出生时都要…

安卓利用CameraX 拍照获这张照片的exif信息

一、首先导入相关权限 <uses-permission android:name"android.permission.WRITE_EXTERNAL_STORAGE" /><uses-featureandroid:name"android.hardware.camera"android:required"true" /><uses-permission android:name"andro…

蓝桥杯练习题总结(三)线性dp题(摆花、数字三角形加强版)

目录 一、摆花 思路一&#xff1a; 确定状态&#xff1a; 初始化&#xff1a; 思路二&#xff1a; 确定状态&#xff1a; 初始化&#xff1a; 循环遍历&#xff1a; 状态转移方程&#xff1a; 二、数字三角形加强版 一、摆花 题目描述 小明的花店新开张&#xff0c;为了吸…

Uni-app/Vue/Js本地模糊查询,匹配所有字段includes和some方法结合使用e

天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 1.第一步 需要一个数组数据 {"week": "全部","hOutName": null,"weekendPrice": null,"channel": "门市价","hOutId": 98,"cTime": "…

【Redis】Redis 内存管理,Redis事务,bigkey和hotkey

目录 Redis 内存管理 缓存数据设置过期时间&#xff1f; Redis 是如何判断数据是否过期的呢&#xff1f; 过期删除策略 内存淘汰机制 主从模式下对过期键的处理&#xff1f; LRU和LFU的区别 Redis事务 定义和原理 Redis 事务的注意点&#xff1f; 为什么不支持回滚&a…