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 图案画笔(笔刷)》


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

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

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

相关文章

分布式消息队列:Rabbitmq(2)

目录 一:交换机 1:Direct交换机 1.1生产者端代码: 1.2:消费者端代码: 2:Topic主题交换机 2.1:生产者代码: 2.2:消费者代码: 二:核心特性 2.1:消息过期机制 2.1.1:给队列中的全部消息指定过期时间 2.1.2:给某条消息指定过期时间 2.2:死信队列 一:交换机 1:Direct交…

elasticsearch-5.6.15集群部署,如何部署x-pack并添加安全认证

目录 一、环境 1、JDK、映射、域名、三墙 2、三台服务器创建用户、并为用户授权 二、配置elasticsearch-5.6.15实例 1、官网获取elasticsearch-5.6.15.tar.gz&#xff0c;拉取到三台服务器 2、elas环境准备 3、修改elasticsearch.yml配置 4、修改软、硬件线程数 5、修改…

GAMP源码阅读(中)伪距单点定位 SPP

原始 Markdown文档、Visio流程图、XMind思维导图见&#xff1a;https://github.com/LiZhengXiao99/Navigation-Learning 文章目录 一、SPP 解算1、spp()&#xff1a;单点定位主入口函数2、estpos()3、estpose_()4、valsol()&#xff1a;GDOP和卡方检验结果有效性 二、卫星位置钟…

基于SSM的n省出口基地公共信息服务平台设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

vue3-vite-ts-pinia

Vue3 vite Ts pinia 实战 源码 electron 仓库地址&#xff1a;https://gitee.com/szxio/vue3-vite-ts-pinia 视频地址&#xff1a;小满Vue3&#xff08;课程导读&#xff09;_哔哩哔哩_bilibili 课件地址&#xff1a;Vue3_小满zs的博客-CSDN博客 初始化Vue3项目 方式一 …

【计算机网络笔记】DNS报文格式

DNS 提供域名到主机IP地址的映射  域名服务的三大要素&#xff1a;  域&#xff08;Domain&#xff09;和域名(Domain name)&#xff1a; 域指由地 理位置或业务类型而联系在一起的一组计算机构 成。  主机&#xff1a;由域名来标识。域名是由字符和&#xff08;或&a…

【多线程面试题十】、说一说notify()、notifyAll()的区别

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;说一说notify()、notify…

pdf转jpg的方法【ps和工具方法】

pdf转jpg的方法&#xff1a; 1.photoshop办法&#xff1a; pdf直接拖入ps中&#xff0c;另存为*.Jpg文件即可 另外注意的时候&#xff0c;有时候别人给你pdf文件中包含你需要的jpg文件&#xff0c;千万不要截图进入ps中&#xff0c;直接把文件拖入ps中&#xff0c;这样的文件…

皮卡丘RCE靶场通关攻略

皮卡丘RCE靶场通关攻略 文章目录 皮卡丘RCE靶场通关攻略RCE(remote command/code execute)概述远程系统命令执行启动环境漏洞练习第一关exec "ping"第二关 exec "eval" RCE(remote command/code execute)概述 RCE漏洞&#xff0c;可以让攻击者直接向后台服…

el -table 多层级嵌套

只要你后端可以查到数据这个层级可以无限嵌套 这里用了懒加载&#xff0c;每次点击的时候将当前点击的父级id作为查询条件&#xff0c;向后端发送请求&#xff0c;来获取他子级的数据&#xff0c;并不是将所有数据查出来拼接返回的。 前端代码 <el-table:data"dataLis…

基于Ubuntu20.04安装ROS系统

文章目录 一、ROS简介二、ROS安装三、ROS安装测试四、安装问题解决1. sudo rosdepc init&#xff1a;找不到命令2. ERROR: cannot download default sources list from...3. Command roscore not found...4. Resource not found: roslaunch... 一、ROS简介 ROS是用于编写机器人…

O(1) 时间插入、删除和获取随机元素

实现RandomizedSet 类&#xff1a; RandomizedSet() 初始化 RandomizedSet 对象 bool insert(int val) 当元素 val 不存在时&#xff0c;向集合中插入该项&#xff0c;并返回 true &#xff1b;否则&#xff0c;返回 false 。 bool remove(int val) 当元素 val 存在时&#xf…

C# 递归算法使用简介_常用整理

一、递归简介 递归算法是一种直接或者间接调用自身函数或者方法的算法。 递归算法的实质是把问题分解成规模缩小的同类问题的子问题&#xff0c;然后递归调用方法来表示问题的解。递归算法对解决一大类问题很有效&#xff0c;它可以使算法简洁和易于理解。 递归本质是循环&a…

mysql如何查询数据出现的次数

在mysql中&#xff0c;可以利用select语句配合group by和count查询数据出现的次数&#xff0c;count能够返回检索数据的数目&#xff0c;语法为“select 列名,count(*) as count from 表名 group by 列名”。 count函数是用来统计表中或数组中记录的一个函数&#xff0c;count…

自定义控件的子控件布局(onLayout()方法)

onLayout()方法用于指定布局中子控件的位置&#xff0c;该方法通常在自定义的ViewGroup容器中重写。 重写onLayout()方法中的常用方法&#xff1a; getChildCount() 获取子控件数量 getChildAt( int index ) 获取指定index的子控件&#xff0c;返回View view.getVisibilit…

大语言模型系列

国产大模型开源一哥再登场&#xff0c;最强双语LLM「全家桶」级开源&#xff01;340亿参数超越Llama2-70B 为什么说大模型训练很难&#xff1f; - 知乎 GitHub - jeinlee1991/chinese-llm-benchmark: 中文大模型能力评测榜单&#xff1a;覆盖百度文心一言、chatgpt、阿里通义千…

C#学习系列之继承

C#学习系列之继承 啰嗦继承使用特殊基类隐藏方法实际使用总结 啰嗦 基础学习。 继承 一个类派生于另一个基类型&#xff0c;它拥有该基础类型的所有成员字段和函数。A派生于B&#xff0c;继承A的所有东西&#xff0c;同时可以增加自己的东西。 使用 public class parent {p…

bootstrap.yml文件未加载

springcloud2020.X.X版本官方重构了bootstrap引导配置的加载方式 需要添加以下依赖才能提高bootstrap.yml文件加载的优先级 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId&g…

Spring-声明式事务

声明式事务 一、简介1、准备工作2、测试 二、声明式事务概念1、编程式事务2、声明式事务3、基于注解的声明式事务1.测试无事务情况2.加入事务①Transactional注解标识的位置②事务属性&#xff1a;只读③事务属性&#xff1a;超时④事务属性&#xff1a;回滚策略⑤事务属性&…

《Effective C++》知识点(4)--设计与声明

18. 让接口容易被正确使用&#xff0c;不易被误用 18.1 好的接口很容易被正确使用&#xff0c;不容易被误用。你应该在你的所有接口中努力达成这些性质。任何接口如果要求客户必须记得做某些事情&#xff0c;就有着"不正确使用"的倾向。 18.2 "促进正确使用&quo…