Fabric

Fabric


Fabric.js是一个非常好用的Javascript HTML5 canvas库,封装了canvas原生较为复杂的api,在canvas元素的顶部提供交互式对象模型,用于实现图片的变形旋转拖拉拽等功能。
在这里插入图片描述

在线demo: 官网链接


下载

npm install fabric --save

yarn add fabric

期间下载到canvas会用时比较长,推荐使用npm,用yarn有时候会安装失败


初始化

如果无需设置图片边框 也可以将canvas.on整个函数删除

<div class="section" :style="{width: `800px`, height: `500px`}" ref="canvasSectionRef"><canvas ref="canvasRef" width="800" height="500" id="fabricCanvas"></canvas>
</div>
// vue3
import {onMounted, onUnmounted, ref} from 'vue';
import {fabric} from 'fabric';const canvasSectionRef = ref(null)  // canvas 父元素引用
const canvasRef = ref(null)  // canvas 的引用
let canvas     // 用于保存fabric canvas 对象, 不能用ref/*** @description 根据 id 在 canvas 对象中查找出对应的图片对象和其索引(索引同时也是显示层级 即 z-index),在callBack中做对应处理* @param id {string} 图片对象的id* @param callBack {(selectedObject: Object, zIndex: number) => any} 查找到对象后的回调* @returns {undefined}*/
const editImageState = (id, callBack) => {const objList = canvas.getObjects()const zIndex = objList.findIndex((obj) => {return obj.customId === id});const selectedObject = objList[zIndex]if (!selectedObject) returncallBack?.(selectedObject, zIndex)
}/*** @description*  初始化fabric canvas 对象 设置边框颜色 和 四个角控制点的样式*  - 点击时显示蓝色虚线边框*  - 锁定时点击为灰色虚线边框*  - 默认没有选中为透明边框*/
const fabricInit = () => {let selectedObjects = {}; // 使用对象来存储不同图片的选中状态 存储方式为 `key - value` -> `id - boolean`canvas = new fabric.Canvas(canvasRef.value);// 自定义控制点样式fabric.Object.prototype.set({cornerStyle: 'square',cornerColor: '#4191fa',cornerSize: 10,transparentCorners: false,cornerStrokeColor: '#fff',cornerStrokeWidth: 2});// 点击时图片添加边框 不需要可以去除 canvas.on('mouse:down', function (options) {// 当前鼠标点击时获取的对象const targetObject = canvas.findTarget(options.e);if (targetObject && targetObject.type === 'image') {// 获取选中图片的id 用于设置边框样式const currentId = targetObject.customId;if (selectedObjects[currentId]) {// 当前点击的图片已经是选中状态,不执行任何操作} else {// 取消显示当前所有图片的边框 (先全部取消 再按id查找当前点击的图片设置为选中)for (const id in selectedObjects) {// 处理取消选中的逻辑if (selectedObjects.hasOwnProperty(id) && selectedObjects[id]) {editImageState(id, (imageItem) => {imageItem.set({stroke: 'transparent',strokeWidth: 2});})}}selectedObjects = {};// 将当前点击的图片设置为选中状态  显示边框selectedObjects[currentId] = true;editImageState(currentId, (imageItem) => {let color = imageItem.selectable ? '#4191fa' : '#ccc'imageItem.set({stroke: color, // 显示边框strokeWidth: 2 // 边框宽度为2});})}} else {// 点击的是画布空白处,清空选中对象for (const id in selectedObjects) {if (selectedObjects.hasOwnProperty(id) && selectedObjects[id]) {editImageState(id, (imageItem) => {imageItem.set({stroke: 'transparent', // 隐藏边框strokeWidth: 2 // 边框宽度为0});})}}selectedObjects = {};}// 处理完成重新渲染 否则不是马上生效canvas.renderAll();});
}onMounted(() => {fabricInit()
});onUnmounted(() => {canvas = null
})

添加图片

添加图片时需要添加唯一id,方便后续根据id对图片进行操作

// 生成唯一的 ID 用于图片升成
function generateUniqueId() {return 'id_' + +new Date();
}/*** @description 根据url地址在canvas对象中添加图片* @param url {string} 图片地址 在线 or 离线*/
const addImg = (url) => {fabric.Image.fromURL(url, function (img) {img.set({selectable: true, // 禁用选中效果 -> true为不禁用 | false为禁用hasControls: true, // 禁用控制点 -> true为不禁用 | false为禁用borderColor: 'transparent', // 边框颜色strokeDashArray: [5, 5],strokeWidth: 2});// 添加唯一idimg.customId = generateUniqueId();canvas.add(img);});
}

新增了addImg这个函数后就可以在onMounted中使用addImg(图片地址),在canvas中就可以看到效果。

锁定 / 解锁图片(不可选中不可移动)

/*** @description 将canvas对象中对应id的图片对象设置为禁用 并设置边框颜色灰色* @param id {string} 图片对象id*/
const lockImg = (id) => {editImageState(id, (imageItem) => {imageItem.set({selectable: false, // 禁用选中效果hasControls: false, // 禁用控制点stroke: '#ccc',});canvas.discardActiveObject()canvas.renderAll()})
}/*** @description 将canvas对象中对应id的图片对象设置取消禁用 并设置边框颜色蓝色* @param id {string} 图片对象id*/
const unLockImg = (id) => {editImageState(id, (imageItem) => {imageItem.set({selectable: true, // 禁用选中效果hasControls: true, // 禁用控制点stroke: '#4191fa'});canvas.renderAll()})
}

删除图片

/*** @description 将canvas对象中对应id的图片对象删除* @param id {string} 图片对象id*/
const deleteImg = (id) => {editImageState(id, (imageItem) => {canvas.remove(imageItem);canvas.renderAll()})
}


获取图片id

根据id进行修改删除图片的方法都有了,然后就是获取图片id的方法

const selectImgId = ref() // 保存点击时的图片id// 判断元素是否为Canvas元素或其子元素
const isCanvasElement = (element) => {return element instanceof HTMLCanvasElement || element.closest('canvas') !== null;
}/*** @description 查找canvas所有生成的对象(图片)中 点击位置上的对象* @param x {number} 鼠标点击的相对于canvas的 x 坐标* @param y {number} 鼠标点击的相对于canvas的 Y 坐标* @returns {Object|null} 点击处的对象 没有则返回null*/
const findClickedObject = (x, y) => {// 遍历Canvas上的所有对象,判断点击位置是否在对象范围内for (let i = canvas.getObjects().length - 1; i >= 0; i--) {const obj = canvas.item(i);if (obj.containsPoint({x: x, y: y})) {return obj;}}return null;
}const getImgId = (event) => {if (isCanvasElement(event.target)) {// 获取鼠标点击位置相对于 Canvas 元素的坐标const canvasRect = canvasSectionRef.value.getBoundingClientRect();const x = event.clientX - canvasRect.left;const y = event.clientY - canvasRect.top;// 查找点击位置上的对象const targetObject = findClickedObject(x, y);if (targetObject && targetObject.type === 'image') {// 选中的图片id保存起来selectImgId.value = targetObject.customId}}
}onMounted(() => {document.addEventListener('click', getImgId);
});

当点击鼠标左键时会获取到图片id并保存在selectImgId变量中,后续需要设置锁定或删除,只需要调用对应的函数传入selectImgId即可。

同时可以通过设置图片对象的索引值更改显示层级,类似cssz-index,这里不过多赘述。

完整示例代码

<template><div class="section" :style="{width: `800px`, height: `500px`}" ref="canvasSectionRef"><canvas ref="canvasRef" width="800" height="500" id="fabricCanvas"></canvas></div>
</template><script setup>
import {onMounted, onUnmounted, ref, watchEffect} from 'vue';
import {fabric} from 'fabric';const canvasSectionRef = ref(null)  // canvas 父元素引用
const canvasRef = ref(null)  // canvas 的引用
let canvas     // 用于保存fabric canvas 对象, 不能用refconst selectImgId = ref() // 保存点击时的图片idwatchEffect(() => {console.log('selectImgId 更改了', selectImgId.value)
})/*** @description 根据 id 在 canvas 对象中查找出对应的图片对象和其索引(索引同时也是显示层级 即 z-index),在callBack中做对应处理* @param id {string} 图片对象的id* @param callBack {(selectedObject: Object, zIndex: number) => any} 查找到对象后的回调* @returns {undefined}*/
const editImageState = (id, callBack) => {const objList = canvas.getObjects()const zIndex = objList.findIndex((obj) => {return obj.customId === id});const selectedObject = objList[zIndex]if (!selectedObject) returncallBack?.(selectedObject, zIndex)
}// 判断元素是否为Canvas元素或其子元素
const isCanvasElement = (element) => {return element instanceof HTMLCanvasElement || element.closest('canvas') !== null;
}/*** @description 查找canvas所有生成的对象(图片)中 点击位置上的对象* @param x {number} 鼠标点击的相对于canvas的 x 坐标* @param y {number} 鼠标点击的相对于canvas的 Y 坐标* @returns {Object|null} 点击处的对象 没有则返回null*/
const findClickedObject = (x, y) => {// 遍历Canvas上的所有对象,判断点击位置是否在对象范围内for (let i = canvas.getObjects().length - 1; i >= 0; i--) {const obj = canvas.item(i);if (obj.containsPoint({x: x, y: y})) {return obj;}}return null;
}const getImgId = (event) => {if (isCanvasElement(event.target)) {// 获取鼠标点击位置相对于 Canvas 元素的坐标const canvasRect = canvasSectionRef.value.getBoundingClientRect();const x = event.clientX - canvasRect.left;const y = event.clientY - canvasRect.top;// 查找点击位置上的对象const targetObject = findClickedObject(x, y);if (targetObject && targetObject.type === 'image') {// 选中的图片id保存起来selectImgId.value = targetObject.customId}}
}/*** @description*  初始化fabric canvas 对象 设置边框颜色 和 四个角控制点的样式*  - 点击时显示蓝色虚线边框*  - 锁定时点击为灰色虚线边框*  - 默认没有选中为透明边框*/
const fabricInit = () => {let selectedObjects = {}; // 使用对象来存储不同图片的选中状态 存储方式为 `key - value` -> `id - boolean`canvas = new fabric.Canvas(canvasRef.value);// 自定义控制点样式fabric.Object.prototype.set({cornerStyle: 'square',cornerColor: '#4191fa',cornerSize: 10,transparentCorners: false,cornerStrokeColor: '#fff',cornerStrokeWidth: 2});// 点击时图片添加边框 不需要可以去除canvas.on('mouse:down', function (options) {// 当前鼠标点击时获取的对象const targetObject = canvas.findTarget(options.e);if (targetObject && targetObject.type === 'image') {// 获取选中图片的id 用于设置边框样式const currentId = targetObject.customId;if (selectedObjects[currentId]) {// 当前点击的图片已经是选中状态,不执行任何操作} else {// 取消显示当前所有图片的边框 (先全部取消 再按id查找当前点击的图片设置为选中)for (const id in selectedObjects) {// 处理取消选中的逻辑if (selectedObjects.hasOwnProperty(id) && selectedObjects[id]) {editImageState(id, (imageItem) => {imageItem.set({stroke: 'transparent',strokeWidth: 2});})}}selectedObjects = {};// 将当前点击的图片设置为选中状态  显示边框selectedObjects[currentId] = true;editImageState(currentId, (imageItem) => {let color = imageItem.selectable ? '#4191fa' : '#ccc'imageItem.set({stroke: color, // 显示边框strokeWidth: 2 // 边框宽度为2});})}} else {// 点击的是画布空白处,清空选中对象for (const id in selectedObjects) {if (selectedObjects.hasOwnProperty(id) && selectedObjects[id]) {editImageState(id, (imageItem) => {imageItem.set({stroke: 'transparent', // 隐藏边框strokeWidth: 2 // 边框宽度为0});})}}selectedObjects = {};}// 处理完成重新渲染 否则不是马上生效canvas.renderAll();});
}// 生成唯一的 ID 用于图片升成
function generateUniqueId() {return 'id_' + +new Date();
}/*** @description 根据url地址在canvas对象中添加图片* @param url {string} 图片地址 在线 or 离线*/
const addImg = (url) => {fabric.Image.fromURL(url, function (img) {img.set({selectable: true, // 禁用选中效果 -> true为不禁用 | false为禁用hasControls: true, // 禁用控制点 -> true为不禁用 | false为禁用borderColor: 'transparent', // 边框颜色strokeDashArray: [5, 5],strokeWidth: 2});// 添加唯一idimg.customId = generateUniqueId();canvas.add(img);});
}onMounted(() => {fabricInit()addImg(new URL('../assets/图片地址.png', import.meta.url).href)document.addEventListener('click', getImgId);
});onUnmounted(() => {canvas = null
})
</script>

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

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

相关文章

【云原生K8s】二进制部署单master K8s+etcd集群

一、实验设计 mater节点master01192.168.190.10kube-apiserver kube-controller-manager kube-scheduler etcd node节点node01192.168.190.20kubelet kube-proxy docker (容…

Matlab之利用MarkerFaceColor来填充marker

matlab画图在加一些marker的时候, 有实心的圆圈, 比如: plot(x,y,.r,MarkerSize,20)但是如果想要一个很大的marker, 就需要把这个markersize调得很大, 比如MarkerSize20 但是也可以用空心的圆圈然后把中间涂上颜色, 这样调整起来更方便. 比如: plot(x,y,or,MarkerSize,5,Mar…

避免安装这5种软件,手机广告频繁弹窗且性能下降

在我们使用手机的日常生活中&#xff0c;选择合适的应用软件对于保持良好的使用体验至关重要。然而&#xff0c;有些软件可能会给我们带来不必要的麻烦和困扰。特别是那些频繁弹窗广告、导致手机性能下降的应用程序&#xff0c;我们应该尽量避免安装它们。 首先第一种&#xf…

Embedding入门介绍以及为什么Embedding在大语言模型中很重要

Embeddings技术简介及其历史概要 在机器学习和自然语言处理中&#xff0c;embedding是指将高维度的数据&#xff08;例如文字、图片、音频&#xff09;映射到低维度空间的过程。embedding向量通常是一个由实数构成的向量&#xff0c;它将输入的数据表示成一个连续的数值空间中…

Python Opencv实践 - 基本图像IO操作

import numpy as np import cv2 as cv import matplotlib.pyplot as plt#读取图像 #cv2.IMREAD_COLOR&#xff1a; 读取彩色图像&#xff0c;忽略alpha通道&#xff0c;也可以直接写1 #cv2.IMREAD_GRAYSCALE: 读取灰度图&#xff0c;也可以直接写0 #cv2.IMREAD_UNCHANGED: 读取…

推荐一个OI的维基百科网站

推荐一个关于OI的维基百科网站&#xff1a; https://oi-wiki.org/ 链接: OI Wiki 这里面有很多关于竞赛的知识&#xff0c;还有各种讲解哦&#xff01;&#xff01;&#xff01; 当然&#xff0c;里面要是有什么看不懂的也可以问我哦&#xff01;&#xff01;&#xff01;

【MySQL】聚合函数与分组查询

文章目录 一、聚合函数1.1 count 返回查询到的数据的数量1.2 sum 返回查询到的数据的总和1.3 avg 返回查询到的数据的平均值1.4 max 返回查询到的数据的最大值1.5 min 返回查询到的数据的最小值 二、分组查询group by2.1 导入雇员信息表2.2 找到最高薪资和员工平均薪资2.3 显示…

【网络编程】利用套接字实现一个简单的网络通信(UDP实现聊天室 附上源码)

网络编程套接字 &#x1f41b;预备知识&#x1f98b;理解源IP地址和目的IP地址&#x1f40c;认识端口号&#x1f41e; 理解 "端口号" 和 "进程ID"&#x1f41c;简单认识TCP协议&#x1f99f;简单认识UDP协议&#x1f997; 什么是网络字节序 &#x1f577;相…

Session与Cookie的区别(五)

储存状态的方式 小明的故事说完了&#xff0c;该来把上面这一段变成网络的实际案例了。其实在网络世界中问题也是一样的。 前面已经提到过我们会把状态存在 Cookie 里面&#xff0c;让 Request 之间能够变得有关联。 假设我们今天要来做一个会员系统&#xff0c;那我要怎么知道…

解读百胜中国2023年第二季度财报:聚焦下沉市场,扩店实力几何?

从全网玩梗的“肯德基疯狂星期四”文学&#xff0c;到大小朋友疯狂抢购的六一三丽鸥玩具联名&#xff0c;再到不久前爆火的必胜客原神联名活动&#xff0c;肯德基、必胜客这两大家喻户晓的快餐品牌&#xff0c;被不少新闻调侃为“顶流制造机”。而近日&#xff0c;这两大顶流背…

Day 75:通用BP神经网络 (2. 单层实现)

代码&#xff1a; package dl;import java.util.Arrays; import java.util.Random;/*** Ann layer.*/ public class AnnLayer {/*** The number of input.*/int numInput;/*** The number of output.*/int numOutput;/*** The learning rate.*/double learningRate;/*** The m…

海外社媒营销:如何树立品牌个性与目标受众共鸣?

随着全球化的不断深入&#xff0c;海外市场对于企业的重要性越来越凸显。在这个数字化时代&#xff0c;社交媒体已经成为品牌塑造和推广的重要渠道之一。然而&#xff0c;海外市场竞争激烈&#xff0c;想要在众多品牌中脱颖而出&#xff0c;就需要在社交媒体关注者的心中树立品…

AcWing1171. 距离(lcatarjan)

输入样例1&#xff1a; 2 2 1 2 100 1 2 2 1输出样例1&#xff1a; 100 100输入样例2&#xff1a; 3 2 1 2 10 3 1 15 1 2 3 2输出样例2&#xff1a; 10 25 #include<bits/stdc.h> using namespace std; typedef long long ll; const int N2e55; int n,m,x,y,k,r…

算法通关村—轻松搞定二叉树的高度和深度问题

1.二叉树的最大深度 二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 1.1 递归 通过上面的步骤能够看出&#xff0c;深度取决于左右子树&#xff0c;只要左子树有&#xff0c;那么高…

gateway过滤器没生效,特殊原因

看这边文章的前提&#xff0c;你要会gateway&#xff0c;知道过滤器怎么配置&#xff1f; 直接来看过滤器&#xff0c;局部过滤器 再来看配置 请求路径 http://127.0.0.1:8080/appframework/services/catalog/catalogSpecials.json?pageindex1&pagesize10&pkidd98…

php-cgi.exe - FastCGI 进程超过了配置的请求超时时限

解决方案一&#xff1a; 处理(php-cgi.exe - FastCGI 进程超过了配置的请求超时时限)的问题 内容转载&#xff1a; 处理(php-cgi.exe - FastCGI 进程超过了配置的请求超时时限)的问题_php技巧_脚本之家 【详细错误】&#xff1a; HTTP 错误 500.0 - Internal Server Error C:…

go编译文件

1.编译go文件 go build [go文件]2.执行文件编译文件 ./demo [demo为go文件名称]

2023-08-06 LeetCode每日一题(24. 两两交换链表中的节点)

2023-08-06每日一题 一、题目编号 24. 两两交换链表中的节点二、题目链接 点击跳转到题目位置 三、题目描述 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0…

Kubernetes高可用集群二进制部署(四)部署kubectl和kube-controller-manager、kube-scheduler

Kubernetes概述 使用kubeadm快速部署一个k8s集群 Kubernetes高可用集群二进制部署&#xff08;一&#xff09;主机准备和负载均衡器安装 Kubernetes高可用集群二进制部署&#xff08;二&#xff09;ETCD集群部署 Kubernetes高可用集群二进制部署&#xff08;三&#xff09;部署…

LeetCode 27题:移除元素

题目 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长…