Fabric.js使用说明Part 2

news/2024/4/27 6:14:14/文章来源:https://blog.csdn.net/qq_40007317/article/details/128938509

目录

一、Fabric.js使用说明Part 1

  • Fabric.js简介

  • 开始

  • 方法

  • 事件

  • canvas常用属性

  • 对象属性

  • 图层层级操作

  • 复制和粘贴

二、Fabric.js使用说明Part 2

  • 锁定

  • 拖拽和缩放画布

  • 分组

  • 动画

  • 图像滤镜

  • 渐变

  • 右键菜单删除

三、Fabric.js使用说明Part 3

  • 自由绘画

  • 绘制背景图片

  • 绘制文本

  • 绘制线和路径

一、锁定

Fabric对象可以添加一些属性进行锁定,例如静止水平移动、静止垂直移动,静止缩放等等

1、静止水平移动(lockMovementX)
let rect = new fabric.Rect({width: 100,height: 50,fill: '#ffde7d',top: 20,left: 20
})
rect.lockMovementX = true
canvas.add(rect)
2、静止垂直移动(lockMovementY)
let rect = new fabric.Rect({width: 100,height: 50,fill: '#ffde7d',top: 20,left: 20
})
rect.lockMovementY = true
3、静止旋转(lockRotation)
let rect = new fabric.Rect({width: 100,height: 50,fill: '#ff9a3c',top: 60,left: 160
})
rect.lockRotation = true
4、静止水平缩放(lockScalingX)
let rect = new fabric.Rect({width: 100,height: 50,fill: '#ffde7d',top: 20,left: 20
})
rect.lockScalingX = true
5、静止垂直缩放(lockScalingY)
let rect = new fabric.Rect({width: 100,height: 50,fill: '#f95959',top: 20,left: 20
})
rect.lockScalingY = true
6、限制拖动区域
let boundingBox = new fabric.Rect({top: 100,left: 100,width: 600,height: 400,fill: '#f95959',selectable: false
})
let movingBox = new fabric.Rect({top: 150,left: 150,width: 100,height: 100,fill: 'yellow',hasBorders: false,hasControls: false,hoverCursor: 'move'
})
this.canvas.add(boundingBox);
this.canvas.add(movingBox);
this.canvas.on("object:moving", (opt) => {let top = movingBox.top;let left = movingBox.left;let topBound = boundingBox.top;let bottomBound = topBound + boundingBox.height;let leftBound = boundingBox.left;let rightBound = leftBound + boundingBox.width;opt.target.left = Math.min(Math.max(left, leftBound), rightBound - movingBox.width)opt.target.top = Math.min(Math.max(top, topBound), bottomBound - movingBox.height)
})

二、分组

Groups是Fabric最强大的功能之一,它可以将任意数量的Fabric对象组合在一起,形成一个小组,分组后,所有对象都可以一起移动、修改、缩放、旋转甚至更改其外观等

let group = new fabric.Group([circle, text], {left: 100,top: 100,angle: -10
})
canvas.add(group)

修改分组的某个对象的属性:

group.item(0).set("fill","red");
group.item(1).set({text:"trololo",fill:"white"
})

分组时要记住的另一件事是对象的状态。例如,在与图像组成组时,需要确保这些图像已完全加载:

fabric.Image.fromURL(logo, img => {let img1 = img.scale(0.3).set({left: 0, top: 0})fabric.Image.fromURL(logo, img => {let img2 = img.scale(0.3).set({left: 80, top: 0})fabric.Image.fromURL(logo, img => {let img3 = img.scale(0.3).set({left: 160, top: 0})let group = new fabric.Group([img1, img2, img3], {left: 10,top: 400})canvas.add(group)})})
})

三、动画

每个Fabric对象都有一个animate方法,该方法可以动画化该对象,animate(动画属性,动画的结束值,[动画的详细信息])

let rect = new fabric.Rect({left: 100,top: 100,width: 100,height: 100,fill: 'red'
})
rect.animate("angle", 45, {onChange: canvas.renderAll.bind(canvas)
})
canvas.add(rect)

第一个参数是要设置动画的属性。第二个参数是动画的结束值。如果矩形具有-15°的角度,并且我们传递了45,则动画将从-15°变为45°。第三个参数是一个可选对象,用于指定动画的详细信息-持续时间,回调,缓动等

rect.animate("angle", 45, {from: 0, // 允许指定可设置动画的属性的起始值(如果我们不希望使用当前值)duration: 1000, // 默认为500(ms),可用于更改动画的持续时间easing: fabric.util.ease.easeOutBounce, // 缓动功能 easeOutBounce、easeInCubic、easeOutCubic、easeInElastic、easeOutElastic、easeInBounce、easeOutExpoonChange: canvas.renderAll.bind(canvas), // 在每次刷新时都会执行onComplete: (e) => { console.log(e) } // 在动画结束时调用的回调
})

animate的一个方便之处在于它还支持相对值

// 向右移动100px
rect.animate('left', '+=100', {onChange: canvas.renderAll.bind(canvas)
})// 逆时针旋转5度
rect.animate('angle', '-=5', {onChange: canvas.renderAll.bind(canvas)
})

四、图像滤镜

fabric.Image的每个实例都具有“ filters”属性,该属性是一个简单的过滤器数组。该阵列中的每个过滤器都是Fabric过滤器之一的实例。或您自己的自定义过滤器的实例。

let url = 'http://localhost:82/public/img/5.png' // 图片url
let base64Url = await this.imgUrlToBase64(url) // 图片base64 url
// 正常照片
fabric.Image.fromURL(url, img => {img.scale(0.3)canvas.add(img)
})// 单个滤镜
fabric.Image.fromURL(base64Url, img => {img.scale(0.3)img.left = 300// 添加滤镜img.filters.push(new fabric.Image.filters.Grayscale())// 图片加载完成之后,应用滤镜效果img.applyFilters()canvas.add(img)
})// 叠加滤镜
fabric.Image.fromURL(base64Url, img => {img.scale(0.3)img.set({left: 300,top: 250,})img.filters.push(new fabric.Image.filters.Grayscale(),new fabric.Image.filters.Sepia(), //色偏new fabric.Image.filters.Brightness({ brightness: 0.2 }) //亮度)img.applyFilters()canvas.add(img)
})

(说明:这里图片可能会出错,放本地图片地址会报“Cannot read property 'naturalWidth' of null”的错误,直接放网络图片地址会报“Failed to execute 'texImage2D' on 'WebGLRenderingContext': The image element contains cross-origin data, and may not be loaded.”的错误。解决方法就是将转为Base64格式

五、渐变

Fabric支持在所有对象上设置填充或描边属性的渐变,首先创建渐变,然后将其分配给填充或描边。

1、线性渐变
// 圆
let circle = new fabric.Circle({left: 100,top: 100,radius: 50,
})
let gradient = new fabric.Gradient({type: 'linear', // linear or radialgradientUnits: 'pixels', // pixels or pencentage 像素 或者 百分比coords: { x1: 0, y1: 0, x2: circle1.width, y2: 0 }, // 至少2个坐标对(x1,y1和x2,y2)将定义渐变在对象上的扩展方式colorStops:[ // 定义渐变颜色的数组{ offset: 0, color: 'red' },{ offset: 0.2, color: 'orange' },{ offset: 0.4, color: 'yellow' },{ offset: 0.6, color: 'green' },{ offset: 0.8, color: 'blue' },{ offset: 1, color: 'purple' },]
})
circle.set('fill', gradient);
canvas.add(circle)
2、径向渐变
let circle = new fabric.Circle({left: 100,top: 100,radius: 50
})
let gradient = new fabric.Gradient({type: 'radial',coords: {r1: 50,r2: 0,x1: 50,y1: 50,x2: 50,y2: 50,},colorStops: [{ offset: 0, color: '#fee140' },{ offset: 1, color: '#fa709a' }]
})
circle.set('fill', gradient);
canvas.add(circle)

六、拖拽和缩放画布

1、拖拽画布
<script>export default {data() {return {lastPosX: 0,       // 上次鼠标位置X坐标lastPosY: 0,       // 上次鼠标位置Y坐标isDragging: false, // 是否可以拖拽画布}},mounted() {... // 初始化canvasthis.canvas.on('mouse:down', this.onMouseDown)this.canvas.on('mouse:move', this.onMouseMove)this.canvas.on('mouse:up', this.onMouseUp)},methods: {// 监听鼠标按下事件onMouseDown(opt) {this.lastPosX = opt.e.clientXthis.lastPosY = opt.e.clientYthis.isDragging = true},// 监听鼠标移动事件onMouseMove(opt) {if (this.isDragging) {this.canvas.viewportTransform[4] += opt.e.clientX - this.lastPosXthis.canvas.viewportTransform[5] += opt.e.clientY - this.lastPosYthis.canvas.requestRenderAll()this.lastPosX = opt.e.clientXthis.lastPosY = opt.e.clientY}},// 监听鼠标松开事件onMouseUp(opt) {if (this.isDragging) {this.canvas.setViewportTransform(this.canvas.viewportTransform)this.isDragging = false}}}}
</script>
2、以画布中心点为基准手动缩放
<template><el-tooltip content="放大" placement="bottom-start"><span class="iconfont icon-fangda" @click="onManualScale(-100)"></span></el-tooltip><el-tooltip content="缩小" placement="bottom-start"><span class="iconfont icon-suoxiao" @click="onManualScale(100)"></span></el-tooltip>
</template>
<script>
export default {// 中心点缩放画布onManualScale(delta) {let zoom = canvas.getZoom() // 获取画布当前缩放值zoom *= 0.999 ** deltazoom = zoom > 10 ? 10 : (zoom < 0.1 ? 0.1 : zoom) // 最大放大10倍,最小缩小至10%canvas.zoomToPoint({ // 以画布中心点为基准缩放x: this.canvasBoxWidth / 2,  // canvasBoxWidth 画布宽度y: this.canvasBoxHeight / 2  // canvasBoxHeight 画布高度}, zoom)}
}
</script>
3、以鼠标指针位置为基准缩放
this.canvas.on('mouse:wheel', this.onMouseWheel)// 监听鼠标放大缩小事件
onMouseWheel(opt) {let delta = opt.e.deltaY // 滚轮,向上滚一下是 -100,向下滚一下是 100let zoom = this.canvas.getZoom() // 获取画布当前缩放值zoom *= 0.999 ** deltazoom = zoom > 10 ? 10 : (zoom < 0.1 ? 0.1 : zoom) // 最大放大10倍,最小缩小至10%this.canvas.zoomToPoint({ // 以鼠标指针位置为基准缩放x: opt.e.offsetX,y: opt.e.offsetY}, zoom)opt.e.preventDefault()opt.e.stopPropagation()
}

七、右键菜单删除

<template><div class="canvas-box" ref="canvasBox"><canvas id="canvas"></canvas><div id="delMenu" ref="delMenu" v-show="isShowDelMenu" :style="delMenuStyle" @contextmenu.prevent=""><el-button type="iconButton" icon="h-icon-delete" @click="handleDeleteMenu">删除</el-button></div></div>
</template>
<script>
export default {name: 'PointerDetail',data () {return {canvas: null,activeEle: null, // 上次选中元素isShowDelMenu: false, // 是否显示删除弹窗delMenuStyle: '' // 删除弹窗定位样式}},mounted() {this.init()},methods: {// 初始化init() {this.canvas = new fabric.Canvas('canvas', {fireRightClick: true, // 启用右键,button的数字为3stopContextMenu: true, // 禁止默认右键菜单})this.canvas.on('mouse:down', this.onMouseDown)},// 监听鼠标按下事件onMouseDown(opt) {// 还原上次选中状态if (this.activeEle) {this.activeEle.set('fill', 'transparent')this.canvas.renderAll()}this.activeEle = opt.target || null// 按下鼠标右键if (opt.button === 3) {// 点击到非图片控件 显示删除弹窗和填充控件背景色if (opt.target && opt.target.type !== 'image') {this.activeEle.set('fill', 'rgba(100, 100, 255, 0.3)')this.canvas.renderAll()this.isShowDelMenu = truethis.$nextTick(() => {this.delMenuStyle = this.getMenuStyle(this.$refs.delMenu, opt)})} else {// 否则隐藏删除弹窗this.hiddenMenu()}// 按下鼠标左键} else {this.hiddenMenu()}},// 获取弹窗坐标getMenuStyle(ele, opt) {let menuWidth = ele.offsetWidthlet menuHeight = ele.offsetHeightlet pointX = opt.pointer.x + 2let pointY = opt.pointer.y + 2if (this.$refs.canvasBox.offsetWidth - pointX <= menuWidth) {pointX -= menuWidth}if (this.$refs.canvasBox.offsetHeight - pointY <= menuHeight) {pointY -= menuHeight}return `left: ${pointX}px; top: ${pointY}px;`},// 隐藏菜单hiddenMenu() {this.activeEle = nullthis.isShowDelMenu = false},// 删除选中元素handleDeleteMenu() {this.canvas.remove(this.activeEle)this.canvas.requestRenderAll()this.hiddenMenu()}}
}

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

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

相关文章

FSM——squirrel状态机使用

FSM——squirrel状态机使用 1 FSM介绍 1.1 概念 FSM&#xff08;finite state machine&#xff09;:有限状态机 是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。核心内容&#xff1a;有限个状态、通过外部操作引起状态的转移。用来对状态的流转进行解耦&a…

高等工程数学张韵华版第二章课后题

答案仅供参考 本章内容 第 2 章 线性空间 2.1 向量的相关性 2.1.1 线性组合和线性表示 2.1.2 线性相关与线性无关 2.2 秩 2.2.1 向量组的秩 2.2.2 矩阵的秩 2.2.3 相抵标准形 2.3 线性空间 2.3.1 线性空间的定义 2.3.2 线性子空间 2.4 维、基、坐标 2.4.1 维、基、坐标的定义…

复杂场景的接口测试

测试场景一&#xff1a;被测业务操作是由多个API调用协作完成 背景&#xff1a;一个单一的前端操作可能会触发后端一系列的API调用&#xff0c;此时API的测试用例就不再是简单的单个API调用&#xff0c;而是一系列API的调用 存在的情况&#xff1a;存在后一个API需要使用前一个…

springboot+vue软件bug项目测试过程管理系统

config&#xff1a;主要用来存储配置文件&#xff0c;以及其他不怎么动用的信息 controller&#xff1a;项目的主要控制文件 dao: 主要用来操作数据库 entity: 实体&#xff0c;用来放与数据库表里对应的实体类&#xff0c;表中的字段对应类中的属性值&#xff0c;并…

视觉SLAM数据集(一):TUM DataSet

首先给出数据集下载地址&#xff1a;TUM Dataset Download。 如果你是第一次做实验&#xff0c;建议下载xyz的数据集&#xff0c;因为它的动作相对很小&#xff0c;只包含桌面上的一小部分。一旦成功测试&#xff0c;就可以试试desk数据集&#xff0c;它包含四张桌子和几个闭环…

C语言的学习小结——数组

一、一维数组的创建与初始化 1、格式&#xff1a; type_t arr_name[const_n];//type_t 是指数组的元素类型 //const_n 是一个常量表达式&#xff0c;用来指定数组的大小 注&#xff1a; 数组是使用下标来访问的&#xff0c;下标从0开始。 数组的大小可以通过计算得到&…

电商平台商品详情接口的应用场景

API接口的定义价格、库存量、发货地点等。此外&#xff0c;它还可以提供商品的详细信息&#xff0c;包括商品的图片、详细描述、规格参数、售后服务等。这些信息可以帮助用户更好地了解商品&#xff0c;从而更好地选择商品。其次&#xff0c;电商平台商品详情接口的实现原理是基…

使用Chemistry Development Kit (CDK) 来进行化学SMILES子结构匹配

摘要 SMILES是一种用于描述化合物结构的字符串表示法&#xff0c;其中子结构搜索是在大规模化合物数据库中查找特定的结构。然而&#xff0c;这种搜索方法存在一个误解&#xff0c;即将化合物的子结构视为一个独立的实体进行搜索&#xff0c;而忽略了它们在更大的化合物中的上…

极光笔记 | 埋点体系建设与实施方法论

PART 01 前 言随着网络技术的发展&#xff0c;从粗犷型到精细化运营型&#xff0c;再到现在的数字化运营&#xff0c;数据变得越来越细分和重要&#xff0c;不仅可以进行策略调整&#xff0c;还可以实现自动化的精细化运营。而数据价值的起点就是埋点&#xff0c;只有合理地埋点…

lammps教程:Ovito选择特定晶粒的方法

大家好&#xff0c;我是小马老师。 本文介绍如何使用ovito提取特定的晶粒。 在多晶的lammps模拟中&#xff0c;可能会对某一个特定晶粒的变形情况进行分析&#xff0c;此时&#xff0c;需要找到这个晶粒&#xff0c;并进行单独分析。 ovito有专用的晶粒识别命令&#xff0c;…

【系统分析师之路】2022上案例分析历年真题

【系统分析师之路】2022上案例分析历年真题 【系统分析师之路】2022上案例分析历年真题【系统分析师之路】2022上案例分析历年真题2022上案例分析历年真题第一题&#xff08;25分&#xff09;2022上案例分析历年真题第二题&#xff08;25分&#xff09;2022上案例分析历年真题第…

RocketMQ 5.x新版本部署优化一览

​ RocketMQ从2022年9月份开始推出了新的5.x大版本。相比于之前的4.x版本&#xff0c;5.x版本向云原生前进了一大步。在增强原因功能的基础上&#xff0c;更是支持多语言客户端&#xff0c;周边生态也进行了补强和完善&#xff0c;明显可以看到离Kafka老大哥又近了很大一步。 …

由Geoscene Enterprise 2.1 升级至Geoscene Enterprise 3.1

文章目录一、升级前工作二、升级Geoscene portal三、升级Web Adaptor&#xff08;针对portal门户&#xff09;四、升级Server 站点&#xff08;作为门户托管服务器&#xff09;五、升级Web Adaptor&#xff08;针对server&#xff09;六、升级Data Store需求&#xff1a;由GeoS…

springboot自定义starter时使用@AutoConfigureBefore、@AutoConfigureAfter的细节问题

正常利用springboot的自动装配 ConfB Configuration(proxyBeanMethodsfalse) public class ConfB {public ConfB(){System.out.println("ConfB构造方式执行...");} }不加spring.factories 项目包结构 此时resources中没有spring.factories 执行结果 2023-02-24…

教你用反射机制如何几分钟搭建完后端

如果想快速搭建后台跨域使用这些技术 反射mybatis-plusjson 反射可以实现动态数据的传输 一般对数据库进行操作肯定离不开这些代码 如果我们用反射机制只需要这一个就行 而说到反射的好处&#xff0c;一般情况下我们做增删改查需要大量的接口才能完成&#xff0c;而用反射我…

通过操作Cortex-A7核,串口输入相应的命令,控制LED灯进行工作

1.通过操作Cortex-A7核&#xff0c;串口输入相应的命令&#xff0c;控制LED灯进行工作 例如在串口输入led1on,开饭led1灯点亮 2.例如在串口输入led1off,开饭led1灯熄灭 3.例如在串口输入led2on,开饭led2灯点亮 4.例如在串口输入led2off,开饭led2灯熄灭 5.例如在串口输入led…

LabVIEW主VI前面板中显示或使用多个子VI

LabVIEW主VI前面板中显示或使用多个子VI想在程序中连接一个或多个子VI的前面板&#xff0c;但是当调用它们时&#xff0c;每个子VI在计算机屏幕上显示为一个新窗口。那么怎么能让每个子VI作为主VI前面板的一部分进行显示&#xff0c;而不是在屏幕上显示多个窗口&#xff1f;正在…

Malware Dev 00 - Rust vs C++ 初探

写在最前 如果你是信息安全爱好者&#xff0c;如果你想考一些证书来提升自己的能力&#xff0c;那么欢迎大家来我的 Discord 频道 Northern Bay。邀请链接在这里&#xff1a; https://discord.gg/9XvvuFq9Wb我会提供备考过程中尽可能多的帮助&#xff0c;并分享学习和实践过程…

mysql 内存架构

1. 背景 从 innodb 的整体架构中可以知道 innodb 的内存架构中分为 buffer pool 缓存区, change pool 修改缓冲区, adaptive hash index 自适应哈希索引, 和 log buffer 日志缓冲区. 2. buffer pool buffer pool 是用于缓冲磁盘页的数据&#xff0c;mysql 的80%的内存会分配给…

GroupDocs.Merger for Java

GroupDocs.Merger for Java GroupDocs.Merger for Java是一个文档操作API&#xff0c;可帮助您合并、拆分、交换或删除文档页面。API通过启用或禁用密码提供保护&#xff0c;并允许开发人员加入PDF、Microsoft Word、Excel和Powerpoint文档。 支持的文件格式 Microsoft Office格…