12 Star 127 Fork 36

mantou / image-editor

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
技术文档.md 13.34 KB
一键复制 编辑 原始数据 按行查看 历史
mantou 提交于 2024-04-15 03:53 . 1、新增插件功能

引言

无界云图是爱趣五科技开源的一款在线图片编辑工具,工具采用 B/S 架构,底层基于 Leafjs(一款高效的 2D Canvas 渲染引擎)做的 开发,该技术文档是为了方便开发者快速接入并进行二次开发所著,文档中有一些专业术语如果有疑问的请告知我们。

技术说明

主要技术&框架如下:

  • React v1.8.2
  • Mobx v5.15.4
  • Vite v4.4.4
  • Semi-Design v2.54.0
  • Leaferjs v1.0.0
  • Typescript v5.1.6

目录结构

src
├─assets 静态资源
│  └─images
├─components 公共组件
│  ├─error-boundary 错误提示
│  ├─list-status 异步列表
│  ├─login 登录/注册模块
│  │  ├─loginMobile 手机登录
│  │  ├─loginQrcode 二维码登录
│  │  └─loginRegisterBox
│  ├─not-found 404页面
│  ├─page-loading 页面顶部的加载条
│  └─water-full 瀑布流组件
├─config 全局配置
├─icons 公共icon
├─language 多语言配置
├─layout 布局框架
│  ├─index-layout
│  └─manage-layout
├─less 全局less
├─pages 页面模块
│  ├─editor 编辑器页面
│  │  ├─common 公共组件
│  │  │  ├─dragitem 拖拽素材到画布
│  │  │  └─source 资源列表框
│  │  ├─components 编辑器大模块
│  │  │  ├─canvas 画布区域组件
│  │  │  ├─contextMenu 鼠标右键菜单
│  │  │  ├─header 编辑器顶部栏
│  │  │  ├─options 设置区域模块
│  │  │  │  ├─components 设置小模块
│  │  │  │  │  ├─align 对齐方式设置
│  │  │  │  │  ├─background 背景设置
│  │  │  │  │  ├─blur 模糊设置
│  │  │  │  │  ├─border 边框设置
│  │  │  │  │  ├─color 颜色设置
│  │  │  │  │  ├─colour 叠加模式设置
│  │  │  │  │  ├─filter 滤镜设置
│  │  │  │  │  ├─flipxy 翻转、复制、锁定、可见等快捷按钮
│  │  │  │  │  ├─group 组设置
│  │  │  │  │  ├─item 公共布局小模块
│  │  │  │  │  ├─opacity 透明度设置
│  │  │  │  │  ├─position 位置设置
│  │  │  │  │  ├─radius 圆角设置
│  │  │  │  │  ├─rotation 旋转设置
│  │  │  │  │  ├─set-color 颜色设置
│  │  │  │  │  ├─shadow 阴影设置
│  │  │  │  │  ├─size 宽高设置
│  │  │  │  │  ├─slider-input 滚动条插件
│  │  │  │  │  ├─strength 强度设置
│  │  │  │  │  ├─text-content 文字内容修改
│  │  │  │  │  └─text-style 文字样式修改
│  │  │  │  └─elements 元素的设置区域功能配置文件
│  │  │  ├─sidebar 左侧菜单
│  │  │  └─sources 左侧资源列表
├─server API服务
├─stores mobx的store管理
├─theme 主题配置
└─utils 公共插件

图层说明

图层包括:图片、文字等元素,图层可以理解为有层级的元素,上面的图层会挡住下面的图层,为了增加项目的扩展性,后续我们会针对 图层做插件化处理,让开发者可以添加自己的图层元素,比如二维码,图表,表格,词云等元素。

  • 【图片】 支持格式 jpg/gif/png/svg
  • 【文本】 支持中英文字体,文本样式支持参考 Leaferjs 的 Text 样式
  • 【组】多个元素进行编组

数据结构

我们使用 JSON 数据来保存工程数据,将工程数据传入渲染内核就可以进行视图的预览、编辑、导出等操作。

工程数据中需要明白页面图层的概念,一个工程中可以包含多个页面,比如我们在设计一个名片的时候,有正面和反面,这就涉及 到 2 个页面,而每个页面下面又有多个图层元素,比如:姓名、LOGO、地址、电话等等、职位。我们可以归纳为:

  1. 【图片图层】LOGO
  2. 【文字图层】姓名、地址、电话、职位

了解完图层、页面的概念,接下来我们了解工程文件,工程文件就是记录页面,元素数据的总文件。工程数据说明请参考内核代 码src/pages/editor/core/types/data.ts中的ViewData,下面是一个工程数据案例:


import type { ViewData, ImageLayer, BasePage, TextLayer } from './core/types/data';

export const tdata: ViewData = {
  name: 'mock数据', // 工程名称
  desc: '暂无描述', // 工程的描述
  version: '1.0.0', // 工程文件当前的版本(版本号是和我们的内核相关的)
  thumb: '', // 工程项目的封面图
  selectPageId: 'p1', // 默认展示的页面id
  createTime: 0, // 创建时间
  updateTime: 0, // 更新时间
  pages: [ // 页面
    {
      id: 'p1',
      name: 'p1',
      desc: '',
      width: 1000, // 页面的尺寸
      height: 600,
      background: { // 页面背景
        type: 'solid',
        color: '#fff',
      },
      layers: [ // 图层
        {
          id: 't1',
          name: 'test',
          desc: '',
          x: 100,
          y: 100,
          opacity: 1,
          rotation: 0,
          flipx: true,
          flipy: false,
          blur: 0,
          type: 'image',
          _dirty: '1', // 用于视图更新
          _lock: false, // 是否锁定
          _hide: false, // 是否隐藏
          width: 200,
          height: 200,
          border: {
            stroke: '#f00', // 边框色
            strokeWidth: 2,
            visible: false,
          },
          shadow: {
            x: 10,
            y: 10,
            blur: 10,
            blendMode: 'normal',
            spread: 0,
            color: 'rgba(0,0,0,0.5)',
            box: false,
            visible: true,
          },
          naturalWidth: 200,
          naturalHeight: 285,
          url: 'https://cdn.h5ds.com/video/uploads/9715/20240207/679367666016878592.png', // 图片链接
          cornerRadius: [200, 200, 200, 200], // 圆角
        } as ImageLayer,
        {
          id: 't3',
          name: 'txt',
          desc: '',
          x: 100,
          y: 100,
          opacity: 1,
          rotation: 0,
          blur: 0,
          type: 'text',
          text: '测试文字,无界云PS',
          fill: null,
          blendMode: 'normal',
          fontFamilyURL: '',
          textStyle: {
            fontSize: 20,
            fill: {
              type: 'linear',
              from: { x: 0, y: 0 },
              to: { x: 1, y: 0 },
              stops: [
                { offset: 0, color: '#FF4B4B' },
                { offset: 1, color: '#FEB027' },
              ],
            },
          },
          _dirty: '1', // 用于视图更新
          _lock: false, // 是否锁定
          _hide: false, // 是否隐藏
          border: {
            stroke: '#f00', // 边框色
            strokeWidth: 2,
            visible: false,
          },
          shadow: {
            x: 10,
            y: 10,
            blur: 10,
            blendMode: 'normal',
            spread: 0,
            color: 'rgba(0,0,0,0.5)',
            box: false,
            visible: false,
          },
        } as TextLayer,
      ],
    } as BasePage,
  ],
};

渲染内核

基于 Leaferjs 和 React 封装了一个渲染内核,渲染内核的本质为一个 React 组件,可以快速在项目中使用。

参数 说明 类型 默认值 必填
data 页面模块的 JSON 数据 BasePage -
target 画布放入的 DOM 容器 HTMLDivElement -
env 当前的渲染环境 'editor','preview' 'editor'
resourceHost 资源文件的 HOST 地址,资源存放路径 string ''
callback 渲染后的回调函数 Store => void -
onControlSelect 选中元素会触发 (_event, ids: string[]) => void -
onDragUp 鼠标弹起来会触发 () => void -
onContextMenu 鼠标右键会触发 (_event, layers: BaseLayer[]) => void -
  • 渲染内核的使用:
import { View } from './core';

const editor = {...};

return (
  <View
    resourceHost="https://cdn.h5ds.com"
    callback={_store => {
      editor.store = _store;
    }}
    env="editor"
    data={pageData}
    target={document.body}
    onContextMenu={(e, layers) => {
      // 显示鼠标右键菜单
      editor.showContextMenu(e.origin, {
        layers,
      });
    }}
    onDragUp={() => {
      // 更新设置区域
      editor.updateOption();
    }}
    onControlSelect={(_e, ids) => {
      // 选中元素
      editor.setSelectedElementIds(ids);
    }}
  />
);
  • 内核 API:

在 callback 回调函数中可以获取到一个store实例,该实例下面存放了很多可以操作画布的方法。API 文档如下:

属性

.data: BasePage

工程数据中的页面数据

.record: RecordParams

操作记录类的实例,内置方法

  • add(n: RecordItem) 添加操作记录
  • debounceAdd(n: RecordItem) 加了防抖函数的记录添加
  • redo() 重做
  • undo() 撤销

.app: ILeafer

Leaferjs 的实例

.editor: IEditorBase

控制器相关业务的实例

.env: ENV

内核当前的运行环境

.resourceHost: string

资源文件的 host 地址

.helper: Object

帮助工具

.utils: Object

小工具方法的集合

方法

.setURL(url: string): string

给 URL 自动添加 resourceHost 配置,比如当前的资源路径是 /assets/img.png,资源实际地址在 cdn 中,这时候我们需要配置 resourceHost 为 cdn 的地址,然后在使用的时候可以调用这个方法得到一个完整的资源路径

const url = '/assets/img.png';

const newURL = store.setURL(url);

// newURL地址为:https://cdn.h5ds.com/assets/img.png;

.getLayerUIByIds(ids: string[]): IUI[]

通过 ID 获取 Leaferjs 的 UI 对象数组

.getLayerByIds(ids: string[]): BaseLayer[]

通过 ID 获取图层的 JSON 数据

.deleteLayers(ids: string[]): void

通过图层 ID 删除图层数据

.groupData(ids: string[]): GroupLayer

将多个图层合并成一个图层,并返回合成后的图层数据

.unGroupData(gid: string): string[]

传入一个组的 id,将该组打散并返回打散后的元素 id

.emitControl(ids: string[]): void

触发指定元素的控制器选中状态,如果传入空数组就会取消所有的控制器

.autoViewSize(): void

根据当前的窗口自动设置画布大小

.setViewSize(scale: number): void;

设置画布的缩放比例

.update(): void

当数据发生变化后,可以调用此方法触发视图的更新

.triggerRotation(elementData: BaseLayer, rotation: number): void

手动设置元素的旋转,解决外部触发旋转不是围绕中心点旋转的问题,旋转元素会导致 x,y 同时发生变化

.capture(params?: IExportOptions): Promise(string);

导出图片

.destroy(): void

销毁实例

数据&视图

数据和画布中的视图做了双向绑定,所以数据改变后只需要调用store.update()就可以触发视图的更新。为什么是手动?有时候我们为 了节约渲染开销,需要做很多数据改动后才会去触发一次 update,所以这里做了类似 react 的 setState,需要我们去手动触发 update 函数来通知视图的更新。

// 修改坐标
layer.x = 100;
layer.y = 200;

// 通知视图更新
store.update();

项目开发

  1. 安装依赖

yarn install

该过程耗时比较长,需要从远程去拉取所需依赖包,安装依赖包成功之后才可以进行接下来的操作。

  1. 进入开发调试模式

yarn dev

项目使用 vite 进行打包和调试,如果需要对项目进行二次开发和修改,可以执行该命令进入开发模式,项目启动成功后浏览器输入 :https://127.0.0.1:3002 进行项目开发和调试。

开发前请注意是否需要修改 vite 的代理配置:


// video-editor/vite.config.ts

server: {
  https: true,
  host: '0.0.0.0',
  port: 3002,
  headers: { // 因为视频编辑器的业务需求,必须加上这两个头信息才可以让ffmpeg在浏览器正常运行
    'Cross-Origin-Opener-Policy': 'same-origin',
    'Cross-Origin-Embedder-Policy': 'credentialless',
  },
  proxy: {
    '/cgi-bin': { // 微信二维码地址代理
      target: 'https://video.h5ds.com',
      changeOrigin: true,
    },
    '/api': { // api地址代理
      target: 'https://video.h5ds.com',
      changeOrigin: true,
    },
    '/fonts': { // 字体包资源的代理地址
      target: 'https://cdn.h5ds.com/assets',
      changeOrigin: true,
    },
    '/video': { // 部分素材的代理地址
      target: 'https://cdn.h5ds.com',
      changeOrigin: true,
    },
  },
},
  1. 打包发布

yarn build

打包成功后会生成 dist 目录,直接部署到服务器即可。具体的部署文档请参考部署说明文档。

JavaScript
1
https://gitee.com/676015863/image-editor.git
git@gitee.com:676015863/image-editor.git
676015863
image-editor
image-editor
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891