基于fabric.js的图片编辑器, 画布背景实现原理

简介: 基于vue3 + fabric.js + vite + element-plus + typescript等技术,画布背景原理分析

![image.png](https://ucc.alicdn.com/pic/developer-ecology/db6vlzf

图片上传

使用了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;
    }
 }

背景设置原理

背景图片和普通图片的区别

  1. 不能选中,不能移动,不能修改,没有操作控件

给背景元素添加以下属性

        hasControls: false,
        hasBorders: false,
        selectable: false,
        lockMovementX: true,
        lockMovementY: true,
        lockScalingX: true,
        lockScalingY: true
  1. 背景需要自适应画布

计算规则如下

  • 获取宽高比例最大值作为元素缩放值,目的是图片元素保持宽高比例不变的情况下覆盖画布
  • 根据缩放值计算图片最新宽高,需要基于画布居中展示
  • 水平居中规则如下,垂直居中同理

image.png

 _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,
    }
  1. 背景图片支持修改,所以上传时需要把画布中的背景元素给移除
 // 获取背景元素
  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);
   }

image.png

  1. 背景元素永远置底,但比画布高一层,所以先置底再上移一层
      this.canvas.sendToBack(this.bgObject);
      this.canvas.bringForward(this.bgObject);
  1. 画布中有元素层级的逻辑,所以当我们选中某个元素的时候需要保持原有层级,但是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;
  }

背景的翻转、分离、删除

支持背景图片的翻转、分离、删除

企业微信截图_d196db79-7919-46b4-ae4d-ffdd5d7af94b.png

翻转

修改背景元素的scaleX属性,默认为水平翻转{ scaleX: -1 }

image.png
基于vue3 + fabric.js + vite + element-plus + typescript等技术,画布背景原理分析

分离

修改背景元素为图片元素

  • type修改为Image
  • 支持选中,移动,修改,有操作控件, 把上文的hasControls等字段取反即可

删除

基于fabric提供的删除方法 this.canvas.remove(target);

简介

vue-design-editor 是仿搞定设计的一款开源图片编辑器, 支持多种格式的导入,包括png、jpg、gif、mp4, 也可以一键psd转模板(后续开发)

github地址

预览

上个开源库是 vue-form-design基于Vue3的可视化表单设计器,拖拽式操作让你快速构建一个表单, 让表单开发简单而高效。

github地址

预览

相关文章
|
1月前
|
自然语言处理 JavaScript 前端开发
探索JavaScript中的闭包:理解其原理与实际应用
探索JavaScript中的闭包:理解其原理与实际应用
19 0
|
3月前
|
前端开发 JavaScript 算法
【面试题】 JavaScript 中的深浅拷贝: 原理与实现
【面试题】 JavaScript 中的深浅拷贝: 原理与实现
|
1月前
|
JavaScript
JS数组增删方法的原理,使用原型定义
JS数组增删方法的原理,使用原型定义
|
2月前
|
存储 JavaScript 前端开发
使用Strve.js来搭建一款 Markdown 编辑器
今天,我们来使用Strve.js来搭建一款 Markdown 编辑器,没错!你没听错。我们需要创建了一个实时 Markdown 编辑器,用户可以在 textarea 中输入 Markdown 文本,然后实时显示转换后的 HTML。你可能会说使用 Strve.js 开发会不会写的特别复杂难懂啊,还不如用 Vue.js 呢!
|
5天前
|
前端开发 JavaScript 编译器
深入解析JavaScript中的异步编程:Promises与async/await的使用与原理
【4月更文挑战第22天】本文深入解析JavaScript异步编程,重点讨论Promises和async/await。Promises用于管理异步操作,有pending、fulfilled和rejected三种状态。通过.then()和.catch()处理结果,但可能导致回调地狱。async/await是ES2017的语法糖,使异步编程更直观,类似同步代码,通过事件循环和微任务队列实现。两者各有优势,适用于不同场景,能有效提升代码可读性和维护性。
|
1月前
|
JavaScript
JS中call()、apply()、bind()改变this指向的原理
JS中call()、apply()、bind()改变this指向的原理
|
1月前
|
JavaScript 前端开发 API
Vue.js 深度解析:nextTick 原理与应用
Vue.js 深度解析:nextTick 原理与应用
|
2月前
|
缓存 JavaScript 前端开发
深入理解Vue.js 3中的响应式原理与使用技巧
【2月更文挑战第1天】Vue.js 3作为一款流行的前端框架,其核心特性之一是响应式数据绑定。本文将深入探讨Vue.js 3中的响应式原理,包括Reactivity API的设计思路和实现原理,并结合实际案例介绍在项目中如何有效地利用Vue.js 3的响应式特性。通过本文的学习,读者将更加全面地理解Vue.js 3的内部工作原理,提升在前端开发中的实践能力。
80 2
|
2月前
|
JavaScript 前端开发 开发者
深入探讨前端框架Vue.js的数据绑定原理
在前端开发中,数据绑定是Vue.js框架的核心特性之一,它实现了视图与数据的双向绑定,极大地提高了开发效率和用户体验。本文将深入探讨Vue.js数据绑定的原理,从响应式数据、依赖追踪到虚拟DOM等方面进行详细分析,帮助读者更好地理解Vue.js框架的工作机制。
23 0
|
2月前
|
存储 缓存 JavaScript
解密前端框架Vue.js的响应式原理
作为当下最流行的前端框架之一,Vue.js的响应式原理是其核心之一。本文将深入探讨Vue.js的响应式原理,从数据劫持、依赖收集到更新视图的完整流程,帮助读者更好地理解Vue.js框架的工作方式。