基于fabric.js的图片编辑器, 画布背景实现原理
图片上传
使用了element-plus提供的图片上传el-upload
组件
<el-upload :show-file-list="false" :auto-upload="false" :on-change="(e) => uploadImage(e, 'background')" >
<button class="right-canvas-resize-btn">上传图片</button>
</el-upload>Ï
图片上传支持两种元素,普通图片元素和背景图片元素,所以定义属性type进行区分
type为Image
为普通图片, background
为背景
背景也是一张图片,使用fabric.Image创建图片元素
因为没有图片服务器所以把上传的文件转换了base64图片,作为fabric图片的链接
需要区分上传类型type
, 不同类型的图片走不同逻辑
fileUpload = async (file: File, name: string, type: string) => {
const src = await this.handler.utils.fileToBase64(file);
if (src) {
const image = new Image();
image.src = src;
const options: any = {
name,
type,
src,
};
await new Promise((resolve) => {
image.onload = () => {
options.width = image.width;
options.height = image.height;
resolve(true);
};
});
let marterialObject;
if (type == "background") {
marterialObject = this.handler.workareaHandler.setBgImage(options);
} else {
marterialObject = this.handler.add(options);
}
return marterialObject;
}
}
背景设置原理
背景图片和普通图片的区别
- 不能选中,不能移动,不能修改,没有操作控件
给背景元素添加以下属性
hasControls: false,
hasBorders: false,
selectable: false,
lockMovementX: true,
lockMovementY: true,
lockScalingX: true,
lockScalingY: true
- 背景需要自适应画布
计算规则如下
- 获取宽高比例最大值作为元素缩放值,目的是图片元素保持宽高比例不变的情况下覆盖画布
- 根据缩放值计算图片最新宽高,需要基于画布居中展示
- 水平居中规则如下,垂直居中同理
_getBgPosition(bgObject: FabricImage | any) {
const { width, height } = this.workspace as any;
let scale = 1;
if (width > bgObject.width || height > bgObject.height) {
if (width / bgObject.width > height / bgObject.height) {
scale = width / bgObject.width;
} else {
scale = height / bgObject.height;
}
}
// 居中
const bgHeight = bgObject.height * scale;
const bgWidth = bgObject.width * scale;
const bgLeft = width / 2 - bgWidth / 2;
const bgTop = height / 2 - bgHeight / 2;
return {
left: bgLeft,
top: bgTop,
scaleX: scale,
scaleY: scale,
}
- 背景图片支持修改,所以上传时需要把画布中的背景元素给移除
// 获取背景元素
getBgObject() {
return this.handler.canvas.getObjects().find((item: any) => {
if (item.type == "background") {
return item;
}
});
}
// 去重, 防止出现多个背景元素
const bgObject = this.getBgObject();
if (bgObject && bgObject.src !== src) {
this.handler.canvas.remove(bgObject);
}
- 背景元素永远置底,但比画布高一层,所以先置底再上移一层
this.canvas.sendToBack(this.bgObject);
this.canvas.bringForward(this.bgObject);
- 画布中有元素层级的逻辑,所以当我们选中某个元素的时候需要保持原有层级,但是fabric.js默认是对象在选中时不保持在当前堆栈位置
所以我们需要在初始化画布时指定保留层级
preserveObjectStacking: true
完整代码如下
async setBgImage(options: WorkareaOption) {
const { src } = options || {};
const editable = false;
const option = {
editable,
hasControls: editable,
hasBorders: editable,
selectable: editable,
lockMovementX: !editable,
lockMovementY: !editable,
lockScalingX: !editable,
lockScalingY: !editable,
hoverCursor: "default",
name: "背景图片",
type: "background",
src,
};
// 去重, 防止出现多个背景元素
const bgObject = this.getBgObject();
if (bgObject && bgObject.src !== src) {
this.handler.canvas.remove(bgObject);
}
const poiOptions = this._getBgPosition(options);
const newOptions = Object.assign({}, option, poiOptions);
this.bgObject = await this.handler.add(newOptions, false);
if (this.bgObject) {
this.canvas.add(this.bgObject);
this.canvas.sendToBack(this.bgObject);
this.canvas.bringForward(this.bgObject);
}
this.canvas.requestRenderAll();
return this.bgObject;
}
背景的翻转、分离、删除
支持背景图片的翻转、分离、删除
翻转
修改背景元素的scaleX属性,默认为水平翻转{ scaleX: -1 }
分离
修改背景元素为图片元素
type
修改为Image
- 支持选中,移动,修改,有操作控件, 把上文的
hasControls
等字段取反即可
删除
基于fabric提供的删除方法 this.canvas.remove(target);
简介
vue-design-editor
是仿搞定设计的一款开源图片编辑器, 支持多种格式的导入,包括png、jpg、gif、mp4, 也可以一键psd转模板(后续开发)
上个开源库是 vue-form-design
基于Vue3的可视化表单设计器,拖拽式操作让你快速构建一个表单, 让表单开发简单而高效。