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

Fabric.js 复制粘贴元素

本文简介

点赞 + 关注 + 收藏 = 学会了


当你要复制一个 fabric 的元素时,你考虑到的是什么?是深拷贝当前选中对象再添加到画布中?


其实,fabric.js 提供了一个克隆方法,在 fabric.js 官网的案例里也有这个demo:Fabric.js demos · Copy and Paste。

这次就讲讲这个 demo。

file



实现思路

动手之前,我们先理清思路。

  1. 要复制元素,首先就得有元素,所以我们在页面创建一些元素(好像再讲废话)。
  2. 复制前,肯定需要有被复制的目标,我们可以使用 canvas.getActiveObject() 方法获取当前被选中的元素。
  3. 复制时,可以使用 clone() 方法,将当前选中的元素对象克隆出来。
  4. 粘贴时,使用 canvas.add() 方法将克隆出来的元素添加到画布中。

当然,实际开发中还有很多需要注意的小点,比如选中一个组的时候要怎么复制粘贴?框选一堆元素时要怎么复制粘贴?

这些问题后面都会讲到,我们先学习如何复制1个元素。



动手编码

理解了前面的思路就能动手了!


复制单个元素

file

<div><button οnclick="copy()">复制</button><button οnclick="paste()">粘贴</button></div><canvas id="c" width="500" height="400" style="border: 1px solid #ccc;"></canvas><script src="https://unpkg.com/fabric@5.3.0/dist/fabric.min.js"></script>
<script>
const canvas = new fabric.Canvas('c')let rect = new fabric.Rect({left: 100,top: 50,fill: '#D81B60',width: 100,height: 100,strokeWidth: 2,stroke: '#880E4F',rx: 10,ry: 10,angle: 45
})canvas.add(rect)// 克隆对象
let _clipboard = null// 复制
function copy() {// 要复制的目标元素let target = canvas.getActiveObject()// 有选中的元素时才进行克隆if (target) {target.clone(function(cloned) {_clipboard = cloned // 将克隆出来的元素赋值给 _clipboard})}
}// 粘贴
function paste() {// 如果克隆对象不存在的话就终止粘贴执行if (!_clipboard) return// 执行粘贴操作,将克隆出来的对象再克隆一遍,然后添加到画布中。_clipboard.clone(function(clonedObj) {// 适当的位移clonedObj.set({left: clonedObj.left + 10,top: clonedObj.top + 10,evented: true, // 当设置为“ false”时,对象不能成为事件的目标。所有事件都会通过它传播。})canvas.add(clonedObj) // 将克隆的元素添加到画布中// 修改克隆对象的位置,以便多次粘贴时更容易观察_clipboard.top += 10_clipboard.left += 10// 将当前选中项修改到新克隆到画布的元素上canvas.setActiveObject(clonedObj)// 刷新画布canvas.requestRenderAll()})
}
</script>

首先在页面中创建2个按钮和1个画布,在画布中创建一个元素。

JS 部分需要创建一个变量保存克隆对象,这个变量叫 _clipboard

在执行复制操作时要判断当前是否选中元素对象。

在执行粘贴操作时要判断当前是否克隆了元素对象。


复制组

其实复制组和复制单个元素时一样的。也是需要获取当前选中的对象,组可以看作是一个元素对象。

代码和上面的一样,只需把单个元素换成组即可,我引用 fabric.js 官网的 demo

file

// 省略部分代码let circle1 = new fabric.Circle({radius: 65,fill: '#039BE5',left: 0
})let circle2 = new fabric.Circle({radius: 65,fill: '#4FC3F7',left: 110,opacity: 0.7
})let group = new fabric.Group([circle1, circle2, ], {left: 40,top: 250
})canvas.add(group)

加上前面的复制粘贴代码即可。


复制框选的元素

复制框选元素的操作会相对复杂一丢丢,但也只是一丢丢而已。

file

因为选中的不止一个元素,所以在粘贴的时候要遍历所有元素出来,用到 fabric.js 提供的 forEachObject 方法。

// 省略部分代码// 粘贴
function paste() {// 如果克隆对象不存在的话就终止粘贴执行if (!_clipboard) return_clipboard.clone(function(clonedObj) {// 适当的位移clonedObj.set({left: clonedObj.left + 10,top: clonedObj.top + 10,evented: true})// 遍历粘贴所有选中的元素clonedObj.canvas = canvasclonedObj.forEachObject(function(obj) {canvas.add(obj)})clonedObj.setCoords()// 适当的位移_clipboard.top += 10_clipboard.left += 10// 将新粘贴出来的元素全部选中canvas.setActiveObject(clonedObj)})
}

最后需要做的就是兼容选中单个元素或者框选多个元素的情况。

获取到当前选中对象后有个 type 属性,当框选多个元素时,type 的值会变成 activeSelection ,我们就可以通过这个来判断当前是选中单个元素还是框选了多个元素。

// 省略部分代码// 粘贴
function paste() {// 如果克隆对象不存在的话就终止粘贴执行if (!_clipboard) return_clipboard.clone(function(clonedObj) {// 适当的位移clonedObj.set({left: clonedObj.left + 10,top: clonedObj.top + 10,evented: true})if (clonedObj.type === 'activeSelection') {// 框选了多个元素// 遍历粘贴所有选中的元素clonedObj.canvas = canvasclonedObj.forEachObject(function(obj) {canvas.add(obj)})clonedObj.setCoords()} else {// 选中一个元素canvas.add(clonedObj)_clipboard.top += 10_clipboard.left += 10}// 适当的位移_clipboard.top += 10_clipboard.left += 10// 将新粘贴出来的元素全部选中canvas.setActiveObject(clonedObj)// 刷新画布canvas.requestRenderAll()})
}

除了上面的鼠标操作外,我们还可以通过监听键盘的 ctrl + cctrl + v(mac监听 command) 来实现上面的效果。

这部分工作留给工友去实现吧,我先溜了。



代码仓库

本文完整代码可通过下方链接获取。

⭐ 复制粘贴元素



推荐阅读

👍《Fabric.js 从入门到_ _ _ _ _ _》

👍《Fabric.js 拖拽顶点修改多边形形状》

👍《Fabric.js 讲解官方demo:Stickman》

👍《Fabric.js 自定义控件》

👍《Fabric.js 样式不更新怎么办?》

👍《Fabric.js 图案画笔(笔刷)》


点赞 + 关注 + 收藏 = 学会了 代码仓库

相关文章:

Fabric.js 复制粘贴元素

本文简介 点赞 关注 收藏 学会了 当你要复制一个 fabric 的元素时&#xff0c;你考虑到的是什么&#xff1f;是深拷贝当前选中对象再添加到画布中&#xff1f; 其实&#xff0c;fabric.js 提供了一个克隆方法&#xff0c;在 fabric.js 官网的案例里也有这个demo&#xff1a…...

rstudio server 服务器卡死了怎么办

欢迎关注weixin:生信小博士 #rstudio 卡死了怎么办 cd ~/.local/share/ ls rm -fr rstudio.old mv ~/.rstudio ~/.rstudio.oldcd ~/.config/ rm -fr .rstudio.old mv ~/.config/rstudio/ ~/.config/rstudio.oldps -ef|grep t040413 |grep rsession |awk {print $2}| xarg…...

贪心算法学习——加油站

目录 一&#xff0c;题目 二&#xff0c;题目接口 三&#xff0c;解题思路及其代码 一&#xff0c;题目 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油…...

Android 字符串工具类

Java基础大佬勿喷&#xff0c;小白专属&#xff01; public class StringUtils {// 判断字符串是否为空public static boolean isEmpty(String str) {return str null || str.trim().isEmpty();}// 判断字符串是否为非空public static boolean isNotEmpty(String str) {retur…...

有了InheritableThreadLocal为啥还需要TransmittableThreadLocal?

有了InheritableThreadLocal为啥还需要TransmittableThreadLocal&#xff1f; 典型回答 InheritableThreadLocal是用于主子线程之间参数传递的&#xff0c;但是&#xff0c;这种方式有一个问题&#xff0c;那就是必须要是在主线程中手动创建的子线程才可以&#xff0c;而现在池…...

结构伪类选择器

伪类选择器&#xff1a;用来描述一个元素的特殊状态&#xff01;比如第一个元素、某个元素的子元素、鼠标点击的元素 1 first-child/last-child /*ul的第一个子元素*/ ul li:first-child{ background: #0f35ad; } /*ul的最后一个子元素*/ ul li:last-child{ background: #0f3…...

java-- 静态数组

1.静态初始化数组 定义数组的时候直接给数组赋值。 2.静态初始化数组的格式&#xff1a; 注意&#xff1a; 1."数据类型[] 数组名"也可以写成"数据类型 数组名[]"。 2.什么类型的数组只能存放什么类型的数据 3.数组在计算机中的基本原理 当计算机遇到…...

世界经济论坛:ChatGPT等生成式AI,对全球23%岗位产生巨大影响

世界经济论坛与全球最大上市咨询公司之一埃森哲合作&#xff0c;联合发布了《未来工作&#xff1a;大语言模型与就业》白皮书。 世界经济论坛表示&#xff0c;随着ChatGPT、Midjourney、Github Copilot等生成式AI的飞速发展&#xff0c;对全球经济和劳动市场产生巨大影响。未来…...

myTracks for Mac:GPS轨迹记录器的强大与便捷

你是否曾经在户外活动或旅行中&#xff0c;希望能够记录下你的移动轨迹&#xff1f;或者在工作中&#xff0c;需要跟踪你的行程路线&#xff1f;myTracks for Mac 是一款强大的 GPS 轨迹记录器&#xff0c;它可以帮助你实现这些愿望。 myTracks 是一款专门为 Mac 设计的 GPS 轨…...

Macos视频增强修复工具:Topaz Video AI for mac

Topaz Video AI是一款使用人工智能技术对视频进行增强和修复的软件。它可以自动降噪、去除锐化、减少压缩失真、提高清晰度等等。Topaz Video AI可以处理各种类型的视频&#xff0c;包括低分辨率视频、老旧影片、手机录制的视频等等。 使用Topaz Video AI非常简单&#xff0c;…...

如何在IDEA中配置指定JDK版本?轻松解决!!!

有时候我们在导入项目&#xff0c;如果手动在IDEA中指定jdk版本&#xff0c;往往启动项目会报错误。 因此当我们新引入项目启动不了时可以检查一下自己IDEA中的jdk版本是否正确。 下面以配置jdk版本为11显示步骤&#xff1a; 1、配置 Project Structure 1.1、通过快捷键&qu…...

思维导图软件 ConceptDraw MINDMAP mac中文特色介绍

ConceptDraw MINDMAP mac是一款思维导图绘制软件&#xff0c;它可以帮助用户快速创建各种类型的思维导图&#xff0c;如组织结构图、流程图、概念图和UML图等。该软件具有直观的界面和简单易用的操作方式&#xff0c;使得用户能够轻松地创建复杂的思维导图。此外&#xff0c;它…...

PDF编辑工具Acrobat Pro DC 2023中文

Acrobat Pro DC 2023是一款全面、高效的PDF编辑和管理软件。它提供了丰富的PDF编辑功能&#xff0c;如创建、编辑、合并、分割、压缩、旋转、裁剪等&#xff0c;让用户可以轻松处理各种PDF文档。同时&#xff0c;该软件还具有智能的PDF处理技术&#xff0c;可以自动识别和修复P…...

如何开通 Medium会员

1 开通 WildCard 卡 首先你需要一张可以支付的外国卡 选择开通 WildCard 卡&#xff0c;优点&#xff1a; 1 无需上传身份证件&#xff0c;支付宝认证即可 2 可以使用国内手机号注册 3 可以使用支付宝、微信充值 开通地址&#xff1a; https://bewildcard.com/card 一步一步…...

CDN是如何一步步壮大到现在这样的

当我们浏览网页、观看在线视频或下载文件时&#xff0c;CDN&#xff08;内容分发网络&#xff09;已经成为网络世界中不可或缺的一部分。本文将探讨CDN的发展历程&#xff0c;其工作原理&#xff0c;以及它如何利用不同地区来提供更快速、可靠的内容交付服务。 CDN的发展历程 过…...

【Java】电子病历编辑器源码(云端SaaS服务)

电子病历编辑器极具灵活性&#xff0c;它既可嵌入到医院HIS系统中&#xff0c;作为内置编辑工具供多个模块使用&#xff0c;也可以独立拿出来&#xff0c;与第三方业务厂商展开合作&#xff0c;为他们提供病历书写功能&#xff0c;充分发挥编辑器的功能。 电子病历基于云端SaaS…...

解决netty作为web,post请求体过大导致413 Request Entity Too Largew问题

问题 项目中使用netty作为web服务&#xff0c;postman请求体内容超出5mb请求netty时&#xff0c;返回413 Request Entity Too Large 解决 查询了一下资料&#xff1a;https://netty.io/4.0/api/io/netty/handler/codec/http/HttpObjectAggregator.html ChannelPipeline p .…...

【Linux】rpm和yum的使用

不知道是不是有和我一样的宝子们&#xff0c;在rpm上卡了老久老久&#xff0c;但其实搞通了&#xff0c;理解了原理之后&#xff0c;不难的&#xff0c;所以不管你现在遇到的困难是什么&#xff0c;都不要放弃&#xff0c;一定要坚持&#xff0c;加油。 一、rpm 1.rpm rpm的…...

贪心算法学习——最大数

目录 ​编辑 一&#xff0c;题目 二&#xff0c;题目接口 三&#xff0c;解题思路级代码 一&#xff0c;题目 给定一组非负整数 nums&#xff0c;重新排列每个数的顺序&#xff08;每个数不可拆分&#xff09;使之组成一个最大的整数。 注意&#xff1a;输出结果可能非常大…...

next项目部署到云服务器上(手动)

准备环境: 云服务器 ECS,服务器安装好了docker 自己的next项目 开始: 1.在next项目根目录下创建Dockerfile文件 FROM node:18-alpine AS base# Install dependencies only when needed FROM base AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333d…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

C++ 类基础:封装、继承、多态与多线程模板实现

前言 C 是一门强大的面向对象编程语言&#xff0c;而类&#xff08;Class&#xff09;作为其核心特性之一&#xff0c;是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性&#xff0c;包括封装、继承和多态&#xff0c;同时讨论类中的权限控制&#xff0c;并展示如何使用类…...

Yii2项目自动向GitLab上报Bug

Yii2 项目自动上报Bug 原理 yii2在程序报错时, 会执行指定action, 通过重写ErrorAction, 实现Bug自动提交至GitLab的issue 步骤 配置SiteController中的actions方法 public function actions(){return [error > [class > app\helpers\web\ErrorAction,],];}重写Error…...

Spring事务传播机制有哪些?

导语&#xff1a; Spring事务传播机制是后端面试中的必考知识点&#xff0c;特别容易出现在“项目细节挖掘”阶段。面试官通过它来判断你是否真正理解事务控制的本质与异常传播机制。本文将从实战与源码角度出发&#xff0c;全面剖析Spring事务传播机制&#xff0c;帮助你答得有…...

DJango知识-模型类

一.项目创建 在想要将项目创键的目录下,输入cmd (进入命令提示符)在cmd中输入:Django-admin startproject 项目名称 (创建项目)cd 项目名称 (进入项目)Django-admin startapp 程序名称 (创建程序)python manage.py runserver 8080 (运行程序)将弹出的网址复制到浏览器中…...