vue-moveable 上手笔记
vue-moveable 上手笔记
1. 简介
moveable
是一个强大的开源web前端交互工具库,github 地址 https://github.com/daybrush/moveable,官方也提供了详尽的 api 文档:文档地址,对于刚接触的新用户可以通过官方提供的 demo 对其有一个直观的了解:demo地址。
moveable
非常适合用来开发一些前端交互项目,如可视化的 UI 编辑工具等,它主要有 12 个功能模块:
- Draggable:拖拽位移
- Resizable:改变元素尺寸大小
- Scalable:元素缩放
- Rotatable:旋转
- Warpable:透视扭曲变形
- Pinchable:捏合交互
- Groupable:分组操作
- Snapable:捕捉吸附
- Clippable:裁切
- Roundable:圆角设置
- OriginDraggable:原点拖拽
- Selecto:多选框选
而 vue-moveable 则是 moveable 的 vue 版本,适合用于 vue 项目中。
2. 基本操作
在 vue 项目中,可以在 data 中定义元素数据用于数据驱动渲染:
data() {
return {
// elements
elements: [{
uuid: 'aaa',
width: 0,
height: 0,
top: 0,
left: 0,
rotate: 0
}, {
uuid: 'bbb',
width: 0,
height: 0,
top: 0,
left: 0,
rotate: 0
}, {
uuid: 'ccc',
width: 0,
height: 0,
top: 0,
left: 0,
rotate: 0
}]
}
},
而配置参数则可以在 computed 计算属性中动态生成:
computed: {
moveableConfig() {
return {
// 【 draggable 】
draggable: true,
throttleDrag: 1, // 步长,默认 0
throttleDragRotate: 0, // 拖拽角度,默认 0 不限制
startDragRotate: 0, // 初始拖拽角度
edgeDraggable: false, // 是否通过拖动边缘线移动,默认 false
// 【 resizable 】
resizable: true,
throttleResize: 1,
// 【 scalable 】
// scalable: true,
throttleScale: 0,
// 【 rotatable 】
rotatable: true,
throttleRotate: 0,
// 【 snappable 】
snappable: true, // 是否自动吸附,默认 false
// snapCenter: true, // 中心吸附
snapHorizontal: true, // 使用水平参考线
horizontalGuidelines: [100, 400, 700],
snapVertical: true, // 使用垂直参考线
verticalGuidelines: [100, 400, 700],
bounds: { // 限制外框范围
left: 0,
right: 700,
top: 0,
bottom: 700
},
// innerBounds: [], // 限制内框范围
snapElement: true,
snapGap: true,
snapThreshold: 5, // default 5
snapDigit: 50,
isDisplaySnapDigit: true,
snapDistFormat: distance = > `距离: $ {distance}`,
// 【 pinchable 】
pinchable: true, // ["draggable", "resizable", "scalable", "rotatable"]
// 【 groupable 】
// groupable: true,
// target: [],
// 【 ... 】
// 【 others 】
keepRatio: true, // 保持比例
className: 'test-control', // 指定控制箱 class
origin: false, // 原点控制箱是否可见
zoom: 1, // 控制箱手柄大小
padding: { // 设置目标边距,以增加可拖动区域
top: 0,
right: 0,
bottom: 0,
left: 0
},
dragArea: false, // 将事件添加到可移动区域,而不是将目标添加到 stopPropagation 的目标 (默认值:false,组中为true)
// container: '',
edge: true
}
}
}
最后是 template 模板,可以在 <Moveable>
组件上绑定所有 api 文档中的事件,如 api 文档中的 onDrag
事件可以绑定为 @drag
。
<template>
<div class="moveable">
<Moveable
ref="moveable"
v-for="element in elements"
:key="element.uuid"
class="wrapper"
v-bind="moveableConfig"
@dragStart="handleTestMoveableEvent"
@drag="handleDrag"
@dragEnd="handleDragEnd"
@dragGroupStart="handleTestMoveableEvent"
@dragGroup="handleTestMoveableEvent"
@dragGroupEnd="handleTestMoveableEvent"
...
...
...
:style="{
width: element.width + 'px',
height: element.height + 'px',
top: element.top + 'px',
left: element.left + 'px',
transform: `rotate(${element.rotate}deg)`
}"
>
<div class="element" :data-uuid="element.uuid">
<h3>{{element.uuid}}</h3>
<p>{{JSON.stringify(element)}}</p>
</div>
</Moveable>
</div>
</template>
<style scoped>
.moveable {
width: 100%;
height: 100%;
position: relative;
}
.wrapper {
position: absolute;
background-color: #cccccc;
}
.element {
width: 100%;
height: 100%;
position: relative;
overflow: scroll;
word-break: break-all;
text-align: left;
}
</style>
关于数据处理,在 demo 中,可以使用 localStorage 进行缓存
created() {
this.elements.forEach((element) = > {
element.left = parseInt(localStorage.getItem('moveable_left_' + element.uuid)) || 0
element.top = parseInt(localStorage.getItem('moveable_top_' + element.uuid)) || 0
// ...
// ...
// ...
});
},
methods: {
/**
* @name: getElementByTarget
* @desc: 根据 target 对象获取 element 实例
*/
getElementByTarget(target) {
if (target.getElementsByClassName('element').length === 0) { return null }
const uuid = target.getElementsByClassName('element')[0].dataset.uuid
const element = this.elements.find((element) = > { return element.uuid === uuid })
return element
},
/**
* @name: handleDrag
*/
handleDrag({ target, left, top }) {
console.log('onDrag')
const element = this.getElementByTarget(target)
if (!element) { return }
// 基础数据处理
const newTop = Math.round(top);
const newLeft = Math.round(left);
// 设置 data 数据
element.top = newTop
element.left = newLeft
// 存储数据到缓存
localStorage.setItem('moveable_top_' + element.uuid, element.top);
localStorage.setItem('moveable_left_' + element.uuid, element.left);
}
// ...
// ...
// ...
}
3. 常用参数
3.1 开关参数
moveable
中,各个功能模块需要通过开关参数进行启用或禁用,如:
draggable: true,
resizable: true,
scalable: true,
rotatable: true,
snappable: true,
pinchable: true,
groupable: true,
...
3.2 全局参数
keepRatio: true, // 保持比例
className: 'test-control', // 指定控制箱 class
origin: false, // 原点控制箱是否可见
zoom: 1, // 控制箱手柄大小
padding: { // 设置目标边距,以增加可拖动区域
top: 0,
right: 0,
bottom: 0,
left: 0
}
3.3 draggable
draggable: true,
throttleDrag: 1, // 步长,默认 0
throttleDragRotate: 0, // 拖拽角度,默认 0 不限制
startDragRotate: 0, // 初始拖拽角度
edgeDraggable: false, // 是否通过拖动边缘线移动,默认 false
3.4 snappable
snappable: true, // 是否自动吸附,默认 false
// snapCenter: true, // 中心吸附
snapHorizontal: true, // 使用水平参考线
horizontalGuidelines: [100, 400, 700],
snapVertical: true, // 使用垂直参考线
verticalGuidelines: [100, 400, 700],
bounds: {
left: 0,
right: 700,
top: 0,
bottom: 700
}, // 限制外框范围
// innerBounds: [], // 限制内框范围,格式同 bounds
snapElement: true,
snapGap: true,
snapThreshold: 5, // default 5
snapDigit: 50,
isDisplaySnapDigit: true,
snapDistFormat: distance = > `距离: $ { distance }`
3.5 Groupable
当使用分组时,需要设置 targets
参数为数组 Array<HTMLElement | SVGElement>
,并且事件也要进行相应转换,如 dragStart
=> dragGroupStart
,pinchStart
=> pinchGroupStart