这篇文章主要介绍“fabric.js图层功能独立显隐、添加、删除、预览怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“fabric.js图层功能独立显隐、添加、删除、预览怎么实现”文章能帮助大家解决问题。
原理
fabric本身有提供group功能,本意是让你将画布上的一些元素组合起来,这也将成为本次图层功能的基础 既以一个group代表一个图层,画布下第一层children只有图层(group),而在group中,才是用户实际绘制的内容
效果预览
本次demo实现:
用户可手动添加/删除图层
可对每个图层进行独立显隐操作,并反馈到画布中
可对每个图层单独预览
效果图:
小Tips
首先fabric是需要到官方上下载的,在选择你需要的模块后再进行打包
虽然npm上也可以下载,但那不是官方的包,是有网友打包好以后上传的,其中没有包含橡皮擦模块,很可能会不符合你的需求
所以我个人建议你可以自行去官网上打包,然后传到你的私有npm库里,然后就可以通过npm来管理了
fabric自定义打包下载地址:Custom Fabric build — Fabric.js Javascript Canvas Library (fabricjs.com)
fabric的事件文档:Event inspector | Fabric.js Demos (fabricjs.com)
接下来的demo将会通过直接引入的方式来使用fabric,虽然我平时写项目都是ts,但练手demo我个人建议还是js,问就是省事
完整代码
目录:
fabric.js即为官网下载的插件包,这个文件就不放了,大家可以自行去官网打包下载
index.html即本次的页面,主要负责dom的处理,直接扔浏览器运行就可以
sketchpad.js 是对fabric的二次封装,同时也避免在html中写太多fabric功能代码
index.html代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .content { display: flex; } .preview { margin-top: 40px; padding-top: 20px; width: 100%; display: flex; justify-content: center; border-top: 1px solid rgba(0,0,0,0.1); } .preview > img { width: 300px; height: 200px; } .layer-list { width: 300px; display: flex; flex-direction: column; padding-right: 10px; margin-right: 10px; box-sizing: content-box; border-right: 1px solid rgba(0,0,0, 0.2); } .layer { width: 300px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px; border: 1px solid rgba(0,0,0, 0.2); } .layer > img { width: 30px; height: 30px; border: 1px solid rgba(0,0,0,0.1); } </style> </head> <body> <div id="app"> <div class="content"> <!-- 左侧的图层列表 --> <div class="layer-list"> <button @click="addLayer">增加图层</button> <div @click="changeCurrentLayer(item.id)" class="layer" : v-for="item of layers" :key="item.id"> <button @click="changeVisible(item.id)">{{ item.show ? '已显示' : '已隐藏'}}</button> <img :src="item.data"> <span>{{ item.name }}</span> <button @click="deleteLayer(item.id)">删除</button> </div> </div> <!-- 右侧的画板 --> <div class="sketchpad-layout" > <canvas id="sketchpad" width="600" height="400" ></canvas> </div> </div> <!-- 对整张画布进行图片预览 --> <div class="preview"> <button @click="updatePreview">整个画布预览:</button> <img :src="preview"> </div> </div> <!-- 使用vue3来进行ui的渲染,懒得操作dom了 --> <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> <!-- 打包好的fabric文件 --> <script src="./fabric.js"></script> <!-- sketchpad里面就是将fabric封装了一层 --> <script src="./sketchpad.js"></script> <script> const { createApp } = Vue /** 单条的数据类型定义 */ const LayerData = { /** 用于显示的名称 */ name: '图层名称', /** 图层的id,也用于管理图层 */ id: '1111', /** 图层的显示状态 */ show: true, /** 图层的数据,用于显示预览图 */ data: '', } createApp({ data() { return { layers: [],// 图层数组,方便管理 sketchpad: null,// 画板 currentLayer: '',// 当前图层的id preview: '',// 预览图的base64数据 } }, methods: { /** * 改变图层的显示/隐藏 * @param id 图层的id */ changeVisible(id) { const index = this.layers.findIndex(v => v.id === id); if (index > -1) { this.layers[index].show = !this.layers[index].show; } this.sketchpad.changeLayerVisible(id) }, /** * 删除图层 * @param id 图层的id */ deleteLayer(id) { const index = this.layers.findIndex(v => v.id === id); if (index > -1) { this.layers.splice(index, 1) this.sketchpad.deleteLayer(id) } }, /** * 增加图层 */ addLayer() { const item = { ...LayerData } item.id = new Date().getTime() item.name = `图层${this.layers.length + 1}` this.layers.push(item) this.sketchpad.addLayer(item.id) this.changeCurrentLayer(item.id) }, /** 选择当前要操作的图层 */ changeCurrentLayer(id) { this.currentLayer = id this.sketchpad.changeCurrentLayer(id) }, /** * 更新预览图 */ updatePreview() { this.preview = this.sketchpad.getImage() }, /** 图层数据更新的回调 */ onChangeData(id, data) { const index = this.layers.findIndex(v => v.id === id); if (index > -1) { this.layers[index].data = data; } } }, mounted() { this.sketchpad = new Sketchpad('sketchpad', { change: this.onChangeData }); this.addLayer() } }).mount('#app') </script> </body> </html>
sketchpad.js代码
console.log('Sketchpad load'); class Sketchpad { /** fabric实例 */ instance = null; /** 当前所在图层的id */ currentLayer = ''; /** 画布宽度 */ width = 600; /** 画布高度 */ height = 600 /** 事件订阅 */ listeners = { /** * 图层内容变化时的回调 * @param {string} id 图层id * @param {base64} data 图层内容的base64格式 */ change: (id, data) => {} } constructor(id, listeners) { this.instance = new fabric.Canvas(id); this.width = this.instance.width; this.height = this.instance.height; this.instance.isDrawingMode = true; this.listeners.change = listeners.change this.instance.on('object:added', ((options) => { if (options.target.type === 'group') return; const groups = this.instance.getObjects() groups.forEach(v => { if (v.layerId === this.currentLayer && v.type === 'group') { v.addWithUpdate(options.target); this.instance.remove(options.target); this.listeners.change(v.layerId, v.toDataURL({ width: this.width, height: this.height })) } }) })) console.log('Sketchpad init') } /** 添加图层 */ addLayer(id) { const group = new fabric.Group([], { width: this.width, height: this.width, }); // 在这里增加一个自定义属性 layerId ,用于区分图层 group.layerId = id this.instance.add(group) this.currentLayer = id; this.listeners.change(id, group.toDataURL({ width: this.width, height: this.height })) } /** 改变图层的显示/隐藏 */ changeLayerVisible(id) { const groups = this.instance.getObjects() groups.forEach(v => { if (v.layerId === id && v.type === 'group') { v.visible = !v.visible; this.instance.renderAll() // 刷新画布,改变group的visible属性,必须通过刷新画布,才能应用新属性值 } }) } /** 选择要操作的图层 */ changeCurrentLayer(id) { this.currentLayer = id } /** 删除图层 */ deleteLayer(id) { const groups = this.instance.getObjects() groups.forEach(v => { if (v.layerId === id && v.type === 'group') { this.instance.remove(v) this.instance.renderAll() // 刷新画布 } }) } /** 获取画布数据,以img标签可以识别的base64格式 */ getImage() { return this.instance.toDataURL() } }
将以上这两个文件代码直接复制粘贴到编辑器里,然后再去打包个fabric.js也放进编辑器里,就可以运行啦