Editor 元素
图形编辑器,用于编辑操作图形,可移动、缩放、旋转、倾斜,支持多选、框选、编组、 双击进组,锁定、层级,向专业级设计工具看齐。支持自定义 配置、 编辑工具、 内部编辑器。
安装插件
需要安装 编辑器插件、resize 插件 才能使用,点此访问 Github 仓库。
npm install @leafer-in/editor
npm install @leafer-in/resize
pnpm add @leafer-in/editor
pnpm add @leafer-in/resize
yarn add @leafer-in/editor
yarn add @leafer-in/resize
bun add @leafer-in/editor
bun add @leafer-in/resize
或通过 script 标签引入,使用全局变量 LeaferIN.editor 访问插件内部功能。
<script src="https://unpkg.com/@leafer-in/editor@1.5.1/dist/editor.min.js"></script>
<script src="https://unpkg.com/@leafer-in/resize@1.5.1/dist/resize.min.js"></script>
<script>
const { Editor } = LeaferIN.editor
</script>
<script src="https://unpkg.com/@leafer-in/editor@1.5.1/dist/editor.js"></script>
<script src="https://unpkg.com/@leafer-in/resize@1.5.1/dist/resize.js"></script>
<script>
const { Editor } = LeaferIN.editor
</script>
关键属性
target:UI
| UI
[]
设置需要编辑的元素, 默认通过 编辑选择器 自动选取 editable 元素(不用设置 draggable )。
element: UI
选中元素的唯一代表(只读), 可使用 element 元素的布局属性方法直接移动、变换编辑框。
单选时为选中元素, 多选时为模拟元素(代替多个元素),用来同步布局编辑框。
config: IEditorConfig
编辑器配置,应用初始化时通过 editor: {}
配置对象传入。
可通过 app.editor.config 实时修改 ( 特殊情况调用 updateEditTool() 方法立即更新 )。
次要属性
hoverTarget:UI
设置 hover 状态的元素, 默认通过 编辑选择器 自动选取 editable 元素。
mergeConfig: IEditorConfig
实际使用的编辑器配置(只读),实时合并编辑器的 config 与元素的 editConfig,频繁访问会有性能开销。
更多属性
名称 | 描述 |
---|---|
状态 | |
visible | 是否显示编辑器,隐藏后,交互功能也将禁用 |
hittable | 编辑器是否响应交互事件,设为 false 后,将禁用编辑器交互 |
single | 是否只选中了单个元素 |
multiple | 是否选中了多个元素 |
editing | 是否处于编辑状态,选择元素后即进入编辑状态 |
innerEditing | 是否处于内部编辑状态,双击单个元素进入内部编辑状态(有内部编辑器的情况) |
groupOpening | 是否处于打开组状态,双击组可进入打开状态,方便选择组内元素 |
dragging | 是否正在拖拽编辑器,包含拖拽控制点、边 |
列表 | |
list | 当前选中的元素列表(只读),未选中时为空数组 |
leafList | 当前选中的元素列表对象(只读), 未选中时为空列表对象 |
openedGroupList | 当前处于打开状态的组列表对象(只读), 未选中时为空列表对象 |
显示元素 | |
buttons | 按钮组,用于放置自定义按钮,整体 around 对齐, 位于编辑器底部,可以 进行配置 |
editBox | 编辑框,负责编辑框的显示与交互,了解 EditBox、EditPoint 类 |
editTool | 当前使用的编辑工具,用来编辑元素的尺寸、外形,选中元素时会自动载入,可 自定义编辑工具 |
innerEditor | 当前使用的内部编辑器,用来编辑文本、路径等内部细节,通过双击元素打开, 可 自定义内部编辑器 |
selector | 选择器,负责单选、多选、框选元素的交互,渲染元素选中、hover 线框 |
关键方法
名称 | 描述 |
---|---|
选择 | |
select() | 选中元素 |
cancel() | 取消选中元素 |
hasItem() | 是否已选中某个元素 |
addItem() | 新增一个元素到选中列表 |
removeItem() | 从选中列表中移出元素 |
编组 | |
group() | 将选中的元素进行编组 |
ungroup() | 将选中的元素进行解组 |
openGroup() | 打开组, 模拟双击打开组的功能 |
closeGroup() | 关闭组,关闭双击打开的组 |
内部编辑 | |
openInnerEditor() | 打开元素的内部编辑器 |
closeInnerEditor() | 关闭内部编辑器 |
锁定 | |
lock() | 锁定选中的元素 |
unlock() | 解锁选中的元素 |
层级 | |
toTop() | 移动选中元素到最顶层(所属 Group 中) |
toBottom() | 移动选中元素到最底层(所属 Group 中) |
更新 | |
update() | 手动更新编辑器的布局、样式等 |
updateEditBox() | 手动更新编辑框,使其贴合元素,一般用于多选元素对齐后操作。 |
updateEditTool() | 更新编辑工具,选择元素后自动调用此方法 |
变换 | |
move() | 位移选中元素 增量操作 |
flip() | 按轴方向( 世界坐标系) 镜像/翻转选中元素 |
scaleOf() | 围绕 element 元素的原点 origin( box 坐标 )缩放选中元素 增量操作 |
rotateOf() | 围绕 element 元素的原点 origin( box 坐标 )旋转选中元素 增量操作 |
skewOf() | 围绕 element 元素的原点 origin( box 坐标 )倾斜选中元素 增量操作 |
配置 editor
基础 事件 样式 按钮组 光标 选择 控制 启用 内部编辑器
resize 元素
编辑器默认通过修改元素的宽高、路径坐标来调整大小,与专业级的设计工具效果一致。
同时也支持配置通过 fontSize、缩放属性来调整大小, 了解 editSize。
辅助
快捷键
历史记录
事件
编辑器操作事件,通过 app.editor.on() 监听。
名称 | 描述 |
---|---|
EditorEvent | 选中、取消元素事件 |
EditorMoveEvent | 移动事件 |
EditorScaleEvent | 调整大小事件 |
EditorRotateEvent | 旋转事件 |
EditorSkewEvent | 倾斜事件 |
EditorGroupEvent | 编组事件 |
InnerEditorEvent | 内部编辑器事件 |
开始体验
点击选中元素, 支持多选、框选。
将光标移动至控制点或边上,拖动可缩放元素。
将光标移动至控制点外沿,出现方向光标,拖动可旋转元素。
按住Ctrl
/ Command
键,将光标移动至四条边上,拖动可倾斜元素。
注意事项
编辑器必须在 App 中使用, 通过传入 editor 配置,可实现自动创建 app.editor 实例及 app.tree, app.sky 分层结构,并自动将 editor 添加到 sky 层中。
// #图形编辑器 [简洁创建]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
const app = new App({
view: window,
editor: {} // 配置 editor 会自动创建并添加 app.editor 实例、tree 层、sky 层
})
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', cornerRadius: [0, 20, 20, 0] }, 300, 100))
// #图形编辑器 [实现原理]
import { App, Rect } from 'leafer-ui'
import { Editor } from '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
const app = new App({ view: window })
app.tree = app.addLeafer({ type: 'design' }) // 添加 tree 层
app.sky = app.addLeafer() // 添加 sky 层
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', cornerRadius: [0, 20, 20, 0] }, 300, 100))
app.sky.add(app.editor = new Editor()) // 添加图形编辑器,用于选中元素进行编辑操作
示例
点击选中元素
元素必需要有 editable 属性才能被选取, 可通过 app.editor 快速访问编辑器实例。
// #图形编辑器 [editable]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
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)
显示底部旋转控制点
默认会继承基础样式,可以进一步 配置 旋转控制点的样式。
// #图形编辑器 [显示旋转控制点]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
const app = new App({
view: window,
editor: { circle: {} }
})
const rect = Rect.one({ editable: true, fill: '#32cd79', cornerRadius: 30 }, 100, 100)
app.tree.add(rect)
app.editor.select(rect)
显示中间控制点,并修改样式
默认会继承基础样式、旋转角度, 可以精确 设置 每个控制点的样式。
// #图形编辑器 [显示中间控制点,并修改样式]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
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: '#32cd79', cornerRadius: 30 }, 100, 100)
app.tree.add(rect)
app.editor.select(rect)
添加底部固定按钮
元素旋转、翻转后仍保持固定方位,可以 配置 按钮组的方位。
// #图形编辑器 [添加底部固定按钮]
import { App, Rect, Box, PointerEvent } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
const app = new App({
view: window,
editor: { buttonsFixed: true }
})
const rect = Rect.one({ editable: true, fill: '#32cd79' }, 100, 100)
app.tree.add(rect)
app.tree.add(Rect.one({ editable: true, fill: '#32cd79' }, 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.select(rect)
监听选择事件
// #图形编辑器 [选中元素事件]
import { App, Rect } from 'leafer-ui'
import { EditorEvent } from '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
const app = new App({
view: window,
editor: {}
})
app.tree.add(Rect.one({ fill: '#32cd79', editable: true }, 100, 100))
app.tree.add(Rect.one({ fill: '#32cd79', editable: true }, 300, 100))
app.editor.on(EditorEvent.SELECT, (e: EditorEvent) => {
console.log(e.editor.list)
})
import { App, Rect } from 'leafer-ui'
import { EditorEvent } from '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
const app = new App({
view: window,
editor: {}
})
app.tree.add(Rect.one({ fill: '#32cd79', editable: true }, 100, 100))
app.tree.add(Rect.one({ fill: '#32cd79', editable: true }, 300, 100))
app.editor.on(EditorEvent.SELECT, (e) => {
console.log(e.editor.list)
})
手动旋转元素
// #图形编辑器 [手动旋转元素]
import { App, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
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)
创建图形模式
// #图形编辑器 [创建图形]
import { App, DragEvent, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
const app = new App({ view: window, editor: {}, fill: '#333' })
app.tree.add({ tag: 'Text', x: 100, y: 100, text: '2秒后,按下鼠标拖动可创建矩形', fill: '#999', fontSize: 16 })
app.tree.add(Rect.one({ editable: true, fill: '#FEB027', cornerRadius: [20, 0, 0, 20] }, 100, 300))
app.tree.add(Rect.one({ editable: true, fill: '#FFE04B', rotation: 10, cornerRadius: [0, 20, 20, 0] }, 300, 300))
app.editor.select(app.tree.children[2])
setTimeout(() => {
// 2秒后进入创建图形模式 //
app.editor.visible = false
app.tree.hitChildren = false
// 创建矩形(拖拽)
let rect: Rect
app.on(DragEvent.START, () => {
rect = new Rect({ editable: true, fill: '#32cd79' })
app.tree.add(rect)
})
app.on(DragEvent.DRAG, (e: DragEvent) => {
if (rect) rect.set(e.getPageBounds()) // 获取事件在 page 坐标系中绘制形成的包围盒 //
})
}, 2000)
// #图形编辑器 [创建图形]
import { App, DragEvent, Rect } from 'leafer-ui'
import '@leafer-in/editor' // 导入图形编辑器插件 //
import '@leafer-in/viewport' // 导入视口插件 (可选)
const app = new App({ view: window, editor: {}, fill: '#333' })
app.tree.add({ tag: 'Text', x: 100, y: 100, text: '2秒后,按下鼠标拖动可创建矩形', fill: '#999', fontSize: 16 })
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
app.on(DragEvent.START, () => {
rect = new Rect({ editable: true, fill: '#32cd79' })
app.tree.add(rect)
})
app.on(DragEvent.DRAG, (e) => {
if (rect) rect.set(e.getPageBounds()) // 获取事件在 page 坐标系中绘制形成的包围盒 //
})
}, 2000)