首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

使用 fabric.js 开发移动端 H5 图片编辑器

编程知识
2024年09月23日 08:38

大家好,我是开源图片编辑器的 https://github.com/ikuaitu/vue-fabric-editor 的作者,它是一款基于 PC 版本的开源图片编辑器。

最近很多开发者咨询,是否可以将开源图片编辑器改造为一款适用于移动端的 H5 版本图片编辑器,最近 H5 版本的图片编辑器刚刚上线,就将实现思路和产品细节整理成笔记分享出来,供大家参考。

基础

开源的图片编辑器的基本功能都有了,例如切换模板、添加元素、自定义字体等,不过相较于移动端的交互会有很大的差异,做了很多改造,这次笔记主要分享一下移动端图片编辑器实现思路和细节


大纲

  1. 切换模板
  2. 添加图片
  3. 添加组合元素
  4. 设置背景色
  5. 修改画布尺寸
  6. 快捷菜单
  7. 属性工具条
  8. 特效字体
  9. 切换字体
  10. 输入文字
  11. 文字排版
  12. 边框
  13. 阴影
  14. 下载图片

注:部分代码示例为封装后的代码,非 fabric.js 原生方法。

1. 切换模板

编辑器基于 fabric.js 开发,所有的模板都是以 json 的格式存储,切换模板只需要请求详情接口,将 json 格式的数据调添加到画布当中即可,需要注意的点是需要将模板中使用的字体名称,并加载字体文件后再进行渲染,否则字体样式没办法正常渲染。

const loadInfo = async (res: any) => {
  const info = res.data
  templName.value = info.name;
  await canvasEditor.getFontList(JSON.stringify(info.json));
  canvasEditor.loadJSON(JSON.stringify(info.json), () => LoadingPlugin(false));
};

2. 添加图片

fabric.js 中添加图片提供了很多种方法,我们使用通过最简单的fabric.Image.fromURL即可,另外,经常有图片尺寸大于画布的情况,还需要将图片按画布宽度的一般进行缩放,更方便用户操作。

const toEditor = async (e: MouseEvent) => {
  visible.value = false
  LoadingPlugin(true)
  const item = await canvasEditor.createImgByElement(e.target as HTMLImageElement)
  await canvasEditor.addBaseType(item, { scale: true })
  LoadingPlugin(false)
}

3. 添加组合元素

fabric.js 支持将单个元素按照 JSON 格式导出/导入,我们将导出的数据存储在数据库中的,导入时按元素类型导入即可,需要获取 JSON 中元素的类型,并作为方法名调用,同样需要在导入前做字体加载,倒入后做缩放。

const capitalizeFirstLetter = (str: string) => {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

const toEditor = async (item: ItemProps) => {
  visible.value = false
  LoadingPlugin(true)
  await canvasEditor.downFontByJSON(JSON.stringify(item.json));
  const el = JSON.parse(JSON.stringify(item.json));
  const elType = capitalizeFirstLetter(el.type);
  new fabric[elType].fromObject(el, (fabricEl: fabric.Object) => {
    canvasEditor.dragAddItem(fabricEl);
    LoadingPlugin(false)
  });
}

4. 设置背景色

设置背景色较为简单,按照 fabric.js 的 API 设置颜色即可,需要注意的是大部分 PC 端的颜色组件并不适配移动端 H5 的场景,不支持 touch 事件,我们使用了 @jaames/iro这个组件,它在移动端表现出色,完全适配我们的场景,而且它的 API 很灵活,我们将它封装成一个通用的颜色组件,在多处调用。

<template>
  <div ref="pickerContainer">
  </div>
</template>

<script setup lang="ts">
import iro from '@jaames/iro';
const emit = defineEmits(['update:modelValue', 'change']);

const props = defineProps({
  modelValue: {
    type: String,
    default: '#000000'
  },
  width: {
    type: Number,
    default: 200
  }
});

const pickerContainer = ref<HTMLButtonElement | string>('');
let colorPicker: any = null;

onMounted(() => {
  // 创建iro.js颜色选择器
  colorPicker = iro.ColorPicker(pickerContainer.value, {
    width: props.width,
    color: props.modelValue,
    borderWidth: 1,
    borderColor: "#fff",
    layoutDirection: 'horizontal',
    layout: [
      {
        component: iro.ui.Slider,
        options: {
          id: 'hue-slider',
          sliderType: 'hue'
        }
      },
      {
        component: iro.ui.Box,
      },
      {
        component: iro.ui.Slider,
        options: {
          sliderType: 'alpha'
        }
      }
    ]
  });

  // 监听颜色变化事件并发射自定义事件
  colorPicker.on('color:change', (color: any) => {
    const rgbaString = color.rgbaString;
    emit('update:modelValue', rgbaString);
    emit('change', rgbaString);
  });
});

</script>

5. 修改画布尺寸

日常使用图片编辑器都有修改画布尺寸的需要,在开源项目中已经封装好了相应的方法,直接调用即可,需要注意的是,当修改尺寸弹框弹出时,为了达到所见即所得的效果,要避免弹框遮挡画布,其他属性修改同理。


const resizeEditor = async () => {
    await nextTick()
    const editorWorkspase = document.querySelector('#workspace') as HTMLElement
    const popElement = document.querySelector('.my-editor-popup') as HTMLElement
    const headerElement = document.querySelector('.t-navbar') as HTMLElement
    if (popElement) {
      editorWorkspase.style.height = `calc(100vh - ${popElement?.offsetHeight + headerElement?.offsetHeight || 0}px)`
    } else {
      editorWorkspase.style.height = ''
    }
  }

6. 快捷菜单

很多快捷操作需要能够让用户快速找的并完成操作,我们为元素添加了快捷菜单功能,避免让一些简单的操作让用户在底部菜单栏点来点去,当选中元素时自动展示,取消选中时隐藏即可,需要注意的是在快捷菜单并不总是在元素上方,快捷菜单应该根据元素位置和画布的尺寸进行定位,当菜单超出画布区域时我们要及时调整菜单位置;另外 当属性弹框出现,画布尺寸变化时,需要同步修改菜单位置。

// 更新位置信息
const upDatePosition = async () => {
  const activeObject = canvasEditor.canvas.getActiveObject();
  if (activeObject) {
    canvasEditor.canvas.renderAll();
    fixLeft.value = 10;
    fixTop.value = 10;
    await nextTick();
    isIncluded(activeObject);
    await nextTick();
  }
}

// 监听选中对象变化更新位置信息
getObjectAttr(upDatePosition)
canvasEditor.canvas.on('selection:updated', upDatePosition)
canvasEditor.canvas.on('mouse:move', upDatePosition)
canvasEditor.on('workspaceAutoEvent', upDatePosition)

7. 属性工具条

参考了其他图片编辑器,部分属性在点击元素后才会出现可修改选项,取消选中时便隐藏选项,另外 选中的元素不同,可修改选项也不同,这是一个在移动端做复杂图片编辑器中非常棒的一个交互。

我们封装了通用的选中类型和方法,针对每个属性组件单独设置隐藏/展示。

8. 特效字体

特效字体主要是文字元素的颜色、边框、阴影的组合,我们将来文字设置样式后的 JSON 导出并保存在数据库中,当选中某一个特效时,将属性按 JSON 中的数据设置给元素即可。

const setStyle = (item: ImgItem) => {
  const activeObject = canvasEditor.canvas.getActiveObjects()[0];
  if (activeObject) {
    const values = toRaw(item.json);
    const keys = ['fill', 'stroke', 'strokeWidth', 'shadow', 'strokeLineCap'];
    activeObject.set('paintFirst', 'stroke');
    keys.forEach((key) => {
      activeObject.set(key, values[key]);
      if (key === 'fill' && typeof values[key] != 'string') {
        activeObject.set(key, new fabric.Gradient(values[key]));
      }
    });
    canvasEditor.canvas.renderAll();
  }
};

9. 切换字体

修改字体只需要调用 fabric.js 元素的fontFamily属性即可,在修改之前要确保字体加载完成。


const changeCommon = async (key: string, value: any) => {
  const activeObject = canvasEditor.canvas.getActiveObjects()[0];
  if (activeObject) {
    LoadingPlugin(true);
    baseAttr.fontFamily = value;
    try {
      await canvasEditor.loadFont(value)
    } catch (error) {
      console.log(error)
    }
    LoadingPlugin(false);
    activeObject && activeObject.set(key, value);
    canvasEditor.canvas.renderAll();
  }
};

10. 输入文字

fabric.js 可直接双击文字元素进行修改,不过在移动端这种交互并不醒目,我们单独为文本元素进行了修改,选中元素后,再次点击时弹出输入框,可以在底部菜单栏点击按钮进行修改。

11. 文字排版

文字排版较为简单,我们只需要按照 fabric.js 的文字属性对文字进行属性设置即可,如 fontSize、lineHeight、charSpacing 等。

// 属性值
const baseAttr = reactive({

  fontSize: 0,
  lineHeight: 0,
  charSpacing: 0,
  textAlign: '',

  fontWeight: '',
  fontStyle: '',

  underline: false,
  linethrough: false,
  overline: false,
});


12. 边框

边框样式和文字样式类似,配合颜色组件可以很快捷的实现功能。

// 属性值
const baseAttr = reactive({
  stroke: '#fff',
  strokeWidth: 0,
  strokeDashArray: [],
});

13. 阴影

引用属性主要是元素的 shadow 子属性的修改,代码如下:

// 属性值
const baseAttr = reactive({
  shadow: {
    color: '#fff',
    blur: 0,
    offsetX: 1,
    offsetY: 1,
  }
});


// 通用属性改变
const changeCommon = () => {
  const activeObject = canvasEditor.canvas.getActiveObjects()[0];
  if (activeObject) {
    activeObject.set('shadow', new fabric.Shadow(baseAttr.shadow));
    canvasEditor.canvas.renderAll();
  }
};

14. 下载图片

fabric.js 可以导出 Png/Jpeg/Base64 格式的图片,同时 JPEG 格式还可以指定图片质量与尺寸倍数,详见 fabric.js 的 API 文档。

结尾

以上就是 fabric.js 开发移动端编辑器的实现细节了,结合我们的开源项目和插件化架构可以很方便的完成项目开发,如果你在做类似项目或者做类似的项目,欢迎与我交流。

开源项目:https://github.com/ikuaitu/vue-fabric-editor/blob/main/README-zh.md

From:https://www.cnblogs.com/nihaojob/p/18426386
本文地址: http://www.shuzixingkong.net/article/2222
0评论
提交 加载更多评论
其他文章 反问面试官3个ThreadLocal的问题
接下来,我想先说说ThreadLocal的用法和使用场景,然后反问面试官3个关于ThreadLocal的话题。
反问面试官3个ThreadLocal的问题 反问面试官3个ThreadLocal的问题 反问面试官3个ThreadLocal的问题
Redis 内存突增时,如何定量分析其内存使用情况
背景 最近碰到一个 case,一个 Redis 实例的内存突增,used_memory最大时达到了 78.9G,而该实例的maxmemory配置却只有 16G,最终导致实例中的数据被大量驱逐。 以下是问题发生时INFO MEMORY的部分输出内容。 #&#160;Memoryused_memory:
Unity中的光源类型(向前渲染路径进行光照计算)
Unity中的光源类型 Unity中共支持4种光源类型: 平行光 点光源 聚光灯 面光源(在光照烘焙时才可以发挥作用) 光源的属性: 位置 方向(到某个点的方向) 颜色 强度 衰减(到某个点的衰减) 平行光 平行光的几何定义是最简单的,平行光可以照亮的范围是无限远的,且对与场景中的各个点的方向和强度
Unity中的光源类型(向前渲染路径进行光照计算) Unity中的光源类型(向前渲染路径进行光照计算) Unity中的光源类型(向前渲染路径进行光照计算)
记一次 RabbitMQ 消费者莫名消失问题的排查
开心一刻 今天好哥们找我借钱哥们:兄弟,我最近手头紧,能不能借我点...我:我手头也不宽裕,要不你试试银行贷款或者花呗?哥们:不行,那个借了要还的我:... 问题回顾 某天下午,生产监控告警:消息积压,队列 xxx 消息数超过 100;我第一时间想到的是应用服务是不是停了,但应用服务存活监控又没有告
记一次 RabbitMQ 消费者莫名消失问题的排查 记一次 RabbitMQ 消费者莫名消失问题的排查 记一次 RabbitMQ 消费者莫名消失问题的排查
GZY.Quartz.MUI(基于Quartz的UI可视化操作组件) 2.7.0发布 新增各项优化与BUG修复
前言 时隔大半年,终于抽出空来可以更新这个组件了 (边缘化了,大概要被裁员了) 2.7.0终于发布了~ 更新内容: 1.添加API类任务的超时时间,可以通过全局配置也可以单个任务设置 2.设置定时任务日志查看默认按开始时间倒序 3.添加是否显示控制台日志的全局配置 目前支持两个参数&#160;Sho
GZY.Quartz.MUI(基于Quartz的UI可视化操作组件) 2.7.0发布 新增各项优化与BUG修复 GZY.Quartz.MUI(基于Quartz的UI可视化操作组件) 2.7.0发布 新增各项优化与BUG修复 GZY.Quartz.MUI(基于Quartz的UI可视化操作组件) 2.7.0发布 新增各项优化与BUG修复
idea运行java项目main方法报build failure错误的解决方法
本文简要介绍了当在使用 IntelliJ IDEA 运行 Java 项目的 main 方法时遇到 'Build Failure' 错误,这通常意味着在项目的构建过程中遇到了问题。解决这类问题通常需要系统地检查和调整项目设置、代码、依赖项等多个方面。
2024 年 C# 高效开发:精选实用类库
前言 在平时开发中,好的类库能帮助我们快速实现功能,C#有很多封装好的类库。 本文将介绍一些2024年特别受欢迎的C#类库,并分析各自的优点让我们编程写代码变的更轻松、更快捷。 快来看一看有没有大家常用的类库,欢迎各位小伙伴留言补充。 1、Entity Framework Core Entity F
2024 年 C# 高效开发:精选实用类库
ScanFormer:逐层抵达目标,基于特征金字塔的指代表达理解框架 | CVPR'24
指代表达理解(REC)旨在在图像中定位由自由形式自然语言描述指定的目标对象。尽管最先进的方法取得了令人印象深刻的性能,但它们对图像进行了密集感知,包含与语言查询无关的多余视觉区域,导致额外的计算开销。这启发论文探讨一个问题:能否消除与语言无关的多余视觉区域,以提高模型的效率?现有的相关方法主要侧重于
ScanFormer:逐层抵达目标,基于特征金字塔的指代表达理解框架 | CVPR'24 ScanFormer:逐层抵达目标,基于特征金字塔的指代表达理解框架 | CVPR'24 ScanFormer:逐层抵达目标,基于特征金字塔的指代表达理解框架 | CVPR'24