Skip to content

Editor

图形编辑器,可移动、缩放、旋转、倾斜,支持多选、框选、打组、锁定、层级,可配置样式

支持自定义 编辑工具内部编辑器

关键属性

target:UI | UI[]

设置需要编辑的元素, 默认通过 编辑选择器 自动选取 editable 元素(不用设置 draggable )。

hoverTarget:UI

设置 hover 状态的元素, 默认通过 编辑选择器 自动选取 editable 元素。

visible: boolean

是否显示编辑器, 默认为 true。

隐藏后,交互功能也将禁用。

hittable: boolean

编辑器是否响应交互事件,默认为 true。

设为 false 后,将禁用编辑器交互。

配置属性

config: IEditorConfig

全局编辑器配置,实例化时传入,可实时修改(调用 updateEditTool() 方法更新)。

mergeConfig: IEditorConfig

实际使用的编辑器配置(只读),合并了 config 与元素的 editConfig

只读属性

editing: boolean

是否处于编辑状态,选择元素后即进入编辑状态。

innerEditing: boolean

是否处于内部编辑状态,双击单个元素进入内部编辑状态(有内部编辑器的情况下)。

groupOpening: boolean

是否处于打开组状态,双击组可进入打开状态,方便选择组内元素。

list: UI[]

当前选中的元素列表,未选中时为空数组。

multiple: boolean

是否选中了多个元素。

single: boolean

是否只选中了单个元素。

dragging: boolean

是否正在拖拽编辑器,包含拖拽控制点、边。

高性能列表

leafList: LeafList

当前选中的元素列表, 未选中时为空列表对象.

openedGroupList: LeafList

当前处于打开状态的组列表, 未选中时为空列表对象.

辅助属性

element: UI

选中元素的唯一代表。

单选时为选中元素, 多选时为模拟元素(代替多个元素),一般用来同步布局编辑框。

editBox: EditBox

编辑框,负责编辑框的显示与交互。

buttons: Group

按钮组,放置旋转控制点及自定义按钮,整体 around 对齐, 位于编辑器底部,可以 进行配置

editTool: EditTool

当前使用的编辑工具。

用来编辑元素的尺寸、外形,选中元素时会自动载入,可 自定义编辑工具

innerEditor: InnerEditor

当前使用的内部编辑器。

用来编辑文本、路径等内部细节,通过双击元素打开, 可 自定义内部编辑器

selector: IEditSelect

选择器,负责单选、多选、框选元素的交互,渲染元素选中、hover 线框。

关键方法

选中元素

select ( target:UI | UI[] )

选中元素。

cancel ( )

取消选中元素。

hasItem ( item: UI ): boolean

是否已选中某个元素。

addItem ( item: UI )

新增一个元素到选中列表。

removeItem ( item:UI )

从选中列表中移出元素。

打组

group ( custom: IGroup | IGroupInputData): IGroup

将选中的元素进行打组,支持传入一个自定义的 Group 实例 或 json 对象。

新的 group 将增加如下属性:

ts
group.editable = true
group.hitChildren = false

ungroup ( ): UI[]

将选中的元素进行解组。

开关组

openGroup ( IGroup )

打开组, 模拟双击打开组的功能。

closeGroup ( IGroup )

关闭组。

内部编辑

openInnerEditor( target?: UI )

打开元素的内部编辑器。

closeInnerEditor()

关闭内部编辑器。

锁定

lock ( )

锁定选中的元素。

元素可以被单选,但是不能编辑。

unlock ( )

解锁选中的元素。

层级

toTop ( )

移动选中元素到最顶层(所属 Group 中)。

toBottom ( )

移动选中元素到最底层(所属 Group 中)。

更新

update ( )

手动更新编辑器的布局、样式等。

updateEditTool()

更新编辑工具,选择元素后自动调用此方法。

手动操作

move ( x: number | IPointData, y = 0): void

位移选中元素 增量操作, 支持直接传入 坐标对象

scaleOf ( origin: IPointData | IAlign, scaleX: number, scaleY = scaleX)

element 元素的 inner 坐标 origin 为原点,缩放选中元素 增量操作

rotateOf ( origin: IPointData | IAlign, rotation: number)

element 元素的 inner 坐标 origin 为原点,旋转选中元素 增量操作

skewOf ( origin: IPointData | IAlign, skewX: number, skewY = 0)

element 元素的 inner 坐标 origin 为原点,倾斜选中元素 增量操作

ts
// 当前选中元素
const { element } = app.editor
const box = element.boxBounds

// 元素的实际内容中心点
const center = {
  x: box.x + box.width / 2,
  y: box.y + box.height / 2,
}

// 想缩放到指定 scale, 需除以元素的 scale,如下:
app.editor.scaleOf(center, scale / element.scale)

// 想旋转到指定 rotation, 需减去元素的 rotation,如下:
app.editor.rotateOf(center, rotation - element.rotation)

// 想倾斜到指定 skewX, 需减去元素的 skewX,如下:
app.editor.skewOf(center, skewX - element.skewX)

配置

编辑器配置

快捷键

按住Ctrl / Command 键:

将光标移动至四条边上,拖动可倾斜元素。

将光标移动至控制点上,拖动可旋转元素。

按住 Alt 键 :中心缩放 / 倾斜 (Alt + Shift 键可组合)。

按住 Shift 键:多选 / 固定比例缩放 / 固定方向移动 / 以对角为中心旋转。

调整大小

编辑器默认通过修改元素宽高、路径坐标来调整大小。

同时也支持通过修改缩放来调整大小, 了解 editSize

历史记录

交互操作可通过监听 editor 的 drag.startdrag.end 事件作为记录历史状态的时机。

操作图形会同时产生多个属性的变更,可以在 app.tree 上监听收集元素的变化做历史记录。

ChildEvent

PropertyEvent

DragEvent

事件

编辑器事件,手动操作也会触发。

EditorEvent

EditorMoveEvent

EditorScaleEvent

EditorRotateEvent

EditorSkewEvent

EditorGroupEvent

InnerEditorEvent

编辑工具

用来编辑元素的尺寸、外形,选中元素时会自动载入。

内置了两种编辑工具,可为不同图形元素扩展编辑工具。

EditTool

LineEditTool

自定义编辑工具

内部编辑器

用来编辑文本、路径等内部细节,通过双击元素打开。

可扩展路径编辑器、文本编辑器。

InnerEditor

自定义内部编辑器

继承

Group

API

Editor

示例

点击选中元素

元素必需要有 editable 属性才能被选取, 可通过 app.editor 快速访问编辑器实例。

ts
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor'

const app = new App({ view: window, editor: {} })

const rect1 = Rect.one({ editable: true, fill: '#FEB027', cornerRadius: [20, 0, 0, 20] }, 100, 100) 
const rect2 = Rect.one({ editable: true, fill: '#FFE04B', cornerRadius: [0, 20, 20, 0] }, 300, 100)

app.tree.add(rect1)
app.tree.add(rect2)

显示底部旋转控制点

默认会继承基础样式,可以进一步 设置 旋转控制点的样式。

ts
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor'

const app = new App({  
    view: window,
    editor: { rotatePoint: {} }
})

const rect = Rect.one({ editable: true, fill: 'rgb(50,205,121)', cornerRadius: 30 }, 100, 100)
app.tree.add(rect)

app.editor.target = rect

添加底部固定按钮

元素旋转、翻转后仍保持固定方位,可以 设置 按钮组的方位。

ts
import { App, Rect, Box, PointerEvent } from 'leafer-ui'
import '@leafer-in/editor'

const app = new App({ 
    view: window,
    editor: { buttonsFixed: true }
})

const rect = Rect.one({ editable: true, fill: 'rgb(50,205,121)' }, 100, 100)
app.tree.add(rect)
app.tree.add(Rect.one({ editable: true, fill: 'rgb(50,205,121)' }, 100, 300))

const button = Box.one({  // 添加移除按钮
    around: 'center',
    fill: '#FEB027',
    cornerRadius: 20,
    cursor: 'pointer',
    children: [{ tag: 'Text', fill: 'white', text: '移除', padding: [7, 10] }]
})

app.editor.buttons.add(button)

button.on(PointerEvent.TAP, () => { // 点击删除元素,并取消选择
    app.editor.list.forEach(rect => rect.remove())
    app.editor.target = null
})

app.editor.target = rect

显示中间控制点,并修改样式

默认会继承基础样式、旋转角度, 可以精确 设置 每个控制点的样式。

ts
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor'

const app = new App({  
    view: window,
    editor: {
        point: { cornerRadius: [0, 0, 10, 0] },
        middlePoint: { width: 12, height: 4, cornerRadius: 2 }
    }
})

const rect = Rect.one({ editable: true, fill: 'rgb(50,205,121)', cornerRadius: 30 }, 100, 100)
app.tree.add(rect)

app.editor.target = rect

监听选择事件

ts
import { App, Rect } from 'leafer-ui'
import { EditorEvent } from '@leafer-in/editor'

const app = new App({
    view: window,
    editor: {}
})

app.tree.add(Rect.one({ fill: 'rgb(50,205,121)', editable: true }, 100, 100))
app.tree.add(Rect.one({ fill: 'rgb(50,205,121)', editable: true }, 300, 100))

app.editor.on(EditorEvent.SELECT, (e: EditorEvent) => { 
    console.log(e.editor.list)
})

手动旋转元素

ts
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor'

const app = new App({ view: window, editor: {} })

const rect = Rect.one({ editable: true, fill: '#FEB027', cornerRadius: [20, 0, 0, 20] }, 100, 100)

app.tree.add(rect)
app.tree.add(Rect.one({ editable: true, fill: '#FFE04B', rotation: 10, cornerRadius: [0, 20, 20, 0] }, 300, 100))


app.editor.select(rect) // 选中 rect

setTimeout(() => {

    // 手动旋转到45度 
    const rotation = 45

    // 围绕中心旋转到指定 rotation, 需减去元素的 rotation,如下:
    app.editor.rotateOf('center', rotation - rect.rotation)

}, 2000)

创建图形模式

ts
import { App, DragEvent, Rect } from 'leafer-ui'
import '@leafer-in/editor'


const app = new App({ view: window, editor: {} })

app.tree.add(Rect.one({ editable: true, fill: '#FEB027', cornerRadius: [20, 0, 0, 20] }, 100, 100))
app.tree.add(Rect.one({ editable: true, fill: '#FFE04B', rotation: 10, cornerRadius: [0, 20, 20, 0] }, 300, 100))

app.editor.select(app.tree.children[0])

setTimeout(() => {

    // 2秒后进入创建图形模式 
    app.editor.visible = false
    app.tree.hitChildren = false

    // 创建图形(拖拽)
    let rect: Rect

    app.on(DragEvent.START, () => {
        rect = new Rect({ editable: true, fill: 'rgb(50,205,121)' })
        app.tree.add(rect)
    })

    app.on(DragEvent.DRAG, (e: DragEvent) => {
        if (rect) rect.set(e.getPageBounds())
    })

}, 2000)

Released under the MIT License.