Draggable, rotatable and resizable components based on Vue (2 & 3)
npm i yoyoo-ddr --save
npm i yoyoo-ddr-vue3 --save
import DDR from 'yoyoo-ddr'
import 'yoyoo-ddr/dist/yoyoo-ddr.css'
// vue 3
// import DDR from 'yoyoo-ddr-vue3'
// import 'yoyoo-ddr-vue3/dist/yoyoo-ddr-vue3.css'
export default {
data() {
return {
transform: { x: 100, y: 100, width: 100, height: 100, rotation: 0 },
}
},
render() {
// vue 3 使用 v-model:value={this.transform}
return (
<DDR v-model={this.transform}>
<div style="background:red;width:100%;height:100%">x={this.transform.x}</div>
</DDR>
)
},
}
export default {
data() {
return {
list: [
{ id: 1, active: false, transform: { x: 100, y: 100, width: 100, height: 100, rotation: 0 } },
{ id: 2, active: false, transform: { x: 200, y: 100, width: 100, height: 100, rotation: 0 } },
{ id: 3, active: false, transform: { x: 300, y: 100, width: 100, height: 100, rotation: 0 } },
{ id: 4, active: false, transform: { x: 400, y: 100, width: 100, height: 100, rotation: 0 } },
],
}
},
methods: {
handleActive(id) {
// 如果active属性和该函数的返回值都为false时,组件不会响应鼠标操作
// 将数组内对应id的数据同步为true
this.list = this.list.map((item) => {
if (item.id === id) {
item = { ...item, active: true }
} else if (item.active) {
item = { ...item, active: false }
}
return item
})
return true
},
renderChild(item) {
return <div style="background:red;width:100%;height:100%">Child {item.id}</div>
},
},
render() {
return (
<div>
{this.list.map((item) => {
// 如果直接使用slot插入子组件的方式会导致全量更新
// 可以使用 renderContent 函数,或者在对DDR组件进行一次包装来解决更新问题
return (
<DDR
beforeActive={this.handleActive}
active={item.active}
key={item.id}
id={item.id}
value={item.transform}
renderContent={this.renderChild}
/>
)
})}
</div>
)
},
}
export default {
data() {
return {
transform: { x: 100, y: 100, width: 100, height: 100, rotation: 0 },
}
},
methods: {
handleDrag(event, transform) {
this.transform = transform
},
handleResize(event, transform) {
this.transform = transform
},
handleRotate(event, transform) {
this.transform = transform
},
},
render() {
// 当组件被拖动时transform的值并不会同步。如果要同步,需要监听事件来同步。
// 在大数组渲染下,建议在拖拽结束后进行一次数据同步。可参考Demo项目的数据同步
return (
<DDR onResize={this.handleResize} onRotate={this.handleRotate} onDrag={this.handleDrag} value={this.transform}>
<div style="background:red;width:100%;height:100%">child</div>
</DDR>
)
},
}
- 轻量级,无任何依赖
- 可配置拖拽、旋转、缩放、网格对齐、限制父元素内移动、固定坐标轴移动、等比例缩放
- 容器如果使用了 overflow scroll 也会导致组件拖拽时的位置错误
名称 | 类型 | 默认值 | 描述 |
---|---|---|---|
draggable | boolean | true | 是否可拖拽 |
rotatable | boolean | true | 是否可旋转 |
resizable | boolean | true | 是否可缩放 |
active | boolean | true | 是否可用, |
acceptRatio | boolean | false | 纵横比,单词拼写错误。但是发现太晚了,所以就这样吧 |
parent | boolean | false | 限制在父容器内拖拽,支持拖动和缩放,旋转角度大于 0 不会判断 |
resizeHandler | Array | ['tl','tm','tr','r','br','bm','l','bl'] | 定义缩放控制点 |
handlerSize | number | 11 | 定义缩放控制点 |
minWidth | number | 1 | 可缩放的最小宽度 |
minHeight | number | 1 | 可缩放最小高度 |
maxWidth | number | 100000000 | 可缩放最大宽度 |
maxHeight | number | 100000000 | 可缩放最大高度 |
value | Object | {x:0,y:0,width:100,height:100,rotation:0} | 位置,注意该参数并不是双向绑定的不支持 v-model,但能响应 value 的更新 |
grid | Array | [1,1] | 格式[x,y],支持拖动和缩放对齐。只能为整数 |
axis | String | 'xy' | 指定坐标轴拖动,默认 xy 都可以拖动,仅支持拖动 |
zoom | Number | 1 | 父容器缩放,当拖拽元素的父元素使用 transform:scale 后,应该同步给此属性 |
id | string | undefined | 数组方式渲染时增加的参数,提高性能 |
beforeActive | Function | ()=> false | 数组方式渲染时增加的参数,当元素被点击时会调用该函数并传入 id |
renderContent | Function | ()=> VNode | 数组方式渲染时增加的参数,用于渲染自定义子节点,如果是单个组件使用直接用 slot 就行了 |
- 拖动状态:
ddr-ready-drag
鼠标按下,准备拖动时的 class。ddr-dragging
拖动时的 class - 缩放状态:
ddr-ready-resize
鼠标按下,准备缩放时的 class。ddr-resizing
缩放时的 class - 旋转状态:
ddr-ready-rotate
鼠标按下,准备旋转时的 class。ddr-rotating
旋转时的 class - 选中状态:
active
组件选中时的 class
拖拽、旋转、缩放时会触发一系列事件,该事件都会传入两个参数,第一个参数为原始的事件对象,第二个参数为当前组件的位置信息。
name | args |
---|---|
dragstart | (event,transform)=>{} :void 0 |
drag | (event,transform)=>{} :void 0 |
dragend | (event,transform)=>{} :void 0 |
rotatestart | (event,transform)=>{} :void 0 |
rotate | (event,transform)=>{} :void 0 |
rotateend | (event,transform)=>{} :void 0 |
resizestart | (event,transform)=>{} :void 0 |
resize | (event,transform)=>{} :void 0 |
resizeend | (event,transform)=>{} :void 0 |
- 可以自定义组件,在目录
src/examples/vseditor/component-impl.js
中增加实现并导出。 components.vue 中添加对应的类型即可。 - 可自定义组件属性编辑器 在目录
src/examples/vseditor/prop-inspector.vue
中添加对应的类型并实现即可 - 可从组件列表中拖拽组件到编辑区域进行编辑
- 支持嵌套组件,
component-impl.js
中的Container
组件实际包含了一个editor-view
组件,可无限嵌套 - 编辑器区域的其他功能将以插件的形式提供,方便功能管理
- 支持历史记录回退,组件删除和画布清除等
- 支持编辑器区域框选功能,批量拖拽,批量删除 ,批量创建副本
- 完善编辑器区域的框选功能,已实现组件的批量更新
- 使用 keyboardjs 来支持快捷键触发操作(command z,command shift z,backspace,command d)
- 持续重构代码,通常情况都是先实现再做优化。
- 优化组件付容器缩放问题
该项目会一直不停的完善,其目的主要是通过大量的项目实践给
yoyoo-ddr
增加一些有用的功能,使他尽可能满足大部分场景的需求。
如果在使用该组件时遇到问题,可以加 QQ(2498683974)联系我。欢迎提出宝贵意见和建议
The MIT License (MIT). Please see License File for more information.